Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static keyword in an array function parameter declaration

Here is the explanation of what it means 6.7.6.3/7:

If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

It is not quite clear what it means. I ran the following example:

main.c

#include "func.h"

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

And 2 different implementations of the test_func:

  1. static version

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. non-static version

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];
}

I checked the assembly code compiled with gcc 7.4.0 -O3 of the function in both of the cases and it turned out to be completely identical:

Disassembly of the functions

(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  

Can you give an example where the static keyword gives some benefits (or any differences at all) comparing to non-static counterpart?

like image 714
St.Antario Avatar asked Jun 19 '19 14:06

St.Antario


2 Answers

Here is an example where static actually makes a difference:

unsigned foo(unsigned a[2])
{
    return a[0] ? a[0] * a[1] : 0;
}

clang (for x86-64, with -O3) compiles this to

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

But after replacing the function parameter with unsigned a[static 2], the result is simply

foo:
        mov     eax, dword ptr [rdi + 4]
        imul    eax, dword ptr [rdi]
        ret

The conditional branch is not necessary because a[0] * a[1] evaluates to the correct result whether a[0] is zero or not. But without the static keyword, the compiler cannot assume that a[1] can be accessed, and thus has to check a[0].

Currently only clang does this optimization; ICC and gcc produce the same code in both cases.

like image 152
dpi Avatar answered Oct 20 '22 19:10

dpi


This isn't used much by compilers in my experience, but one use is that the compiler can assume that the (array decayed into pointer) parameter is not NULL.

Given this function, both gcc and clang (x86) produce identical machine code at -O3:

int func (int a[2])
{
  if(a)
    return 1;
  return 0;
}

Disassembly:

func:
        xor     eax, eax
        test    rdi, rdi
        setne   al
        ret

When changing the parameter to int a[static 2], gcc gives the same output as before, but clang does a better job:

func: 
        mov     eax, 1
        ret

Since clang realizes that a can never be NULL, so it can skip the check.

like image 43
Lundin Avatar answered Oct 20 '22 18:10

Lundin