c - 数组函数参数声明中的 static 关键字



以下是对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实现:

  1. 静态版本

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];
} 
  1. 非静态版本

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,因此它可以跳过检查。

相关内容

最新更新