Статическое ключевое слово в объявлении параметра функции массива - PullRequest
6 голосов
/ 19 июня 2019

Вот объяснение того, что это означает 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));
}

И 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];
} 
нестатическая версия

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 дает некоторые преимущества (илиесть ли вообще различия) по сравнению с нестатическим аналогом?

Ответы [ 2 ]

11 голосов
/ 19 июня 2019

Вот пример, где 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] дает правильный результат, независимо от того, равен ли [0] нулю или нет. Но без ключевого слова static компилятор не может предположить, что к [1] ​​можно получить доступ, и поэтому должен проверить [0].

В настоящее время только clang выполняет эту оптимизацию; ICC и gcc выдают одинаковый код в обоих случаях.

6 голосов
/ 20 июня 2019

По моему опыту, это не так часто используется компиляторами, но одно из них заключается в том, что компилятор может предположить, что параметр (массив распался в указатель) не 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, поэтому он может пропустить проверку.

...