数组函数参数声明中的静态关键字

时间:2019-06-19 14:17:57

标签: c gcc assembly language-lawyer x86-64

以下是6.7.6.3/7含义的解释:

  

如果关键字static也出现在数组的[]中   类型派生,然后对于函数的每次调用,   相应的实际参数应提供对第一个参数的访问   数组的元素,其元素的数量至少与   大小表达式。

目前尚不清楚其含义。我运行了以下示例:

main.c

#include "func.h"

int main(void){
    char test[4] = "123";
    printf("%c\n", test_func(2, test));
}

test_func的2种不同实现:

  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  

您能举个例子,与非静态关键字相比,静态关键字能带来一些好处(或根本没有区别)吗?

2 个答案:

答案 0 :(得分:11)

以下是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[1]会计算a [0]是否为零的正确结果,因此不需要条件分支。但是,如果没有static关键字,则编译器无法假定a [1]可以访问,因此必须检查a [0]。

目前只有clang进行此优化;在两种情况下,ICC和gcc都会生成相同的代码。

答案 1 :(得分:6)

根据我的经验,编译器并没有太多使用它,但是一种用法是,编译器可以假定(数组衰减为指针)参数不是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,所以它可以跳过检查。