以下是对6.7.6.3/7含义的解释:
如果关键字
static
也出现在数组的[
和]
中 类型派生,然后对于每次调用函数,值 相应的实际参数应提供对第一个 元素,其中元素的元素至少与 大小表达式。
目前还不清楚这意味着什么。我运行了以下示例:
main.c
#include "func.h"
int main(void){
char test[4] = "123";
printf("%cn", test_func(2, test));
}
和 2 种不同的test_func
实现:
- 静态版本
func.h
char test_func(size_t idx, const char[const static 4]);
func.c
char test_func(size_t idx, const char arr[const static 4]){
return arr[idx];
}
- 非静态版本
func.h
char test_func(size_t idx, const char[const 4]);
func.c
char test_func(size_t idx, const char arr[const 4]){
return arr[idx];
}
在这两种情况下,我检查了使用该函数gcc 7.4.0 -O3
编译的汇编代码,结果证明完全相同:
函数的反汇编
(gdb) disas main
sub rsp,0x18
mov edi,0x2
lea rsi,[rsp+0x4]
mov DWORD PTR [rsp+0x4],0x333231
mov rax,QWORD PTR fs:0x28
mov QWORD PTR [rsp+0x8],rax
xor eax,eax
call 0x740 <test_func>
[...]
(gdb) disas test_func
movzx eax,BYTE PTR [rsi+rdi*1]
ret
您能否举一个例子,与非静态关键字相比,静态关键字提供了一些好处(或任何差异)?
下面是一个static
实际上有所作为的示例:
unsigned foo(unsigned a[2])
{
return a[0] ? a[0] * a[1] : 0;
}
clang (对于 x86-64,带 -O3)将其编译为
foo:
mov eax, dword ptr [rdi]
test eax, eax
je .LBB0_1
imul eax, dword ptr [rdi + 4]
ret
.LBB0_1:
xor eax, eax
ret
但是用unsigned a[static 2]
替换函数参数后,结果很简单
foo:
mov eax, dword ptr [rdi + 4]
imul eax, dword ptr [rdi]
ret
条件分支不是必需的,因为无论 a[0] 是否为零a[0] * a[1]
计算结果都是正确的。但是如果没有static
关键字,编译器就不能假设可以访问 a[1],因此必须检查 a[0]。
目前只有 clang 进行此优化;ICC 和 gcc 在这两种情况下生成相同的代码。
根据我的经验,编译器并不怎么使用它,但一种用途是编译器可以假设(数组衰减为指针)参数不NULL
。
给定这个函数,gcc 和 clang (x86) 都会在-O3
生成相同的机器代码:
int func (int a[2])
{
if(a)
return 1;
return 0;
}
拆卸:
func:
xor eax, eax
test rdi, rdi
setne al
ret
当将参数更改为int a[static 2]
时,gcc 给出的输出与以前相同,但 clang 做得更好:
func:
mov eax, 1
ret
由于 clang 意识到a
永远不可能为 NULL,因此它可以跳过检查。