Подтвердить что ты не робот

Что означает "[*]" (звездный модификатор) в C?

При попытке реализовать парсер C11 (для образовательных целей) я обнаружил, что в C11 (стр. 470), но также и в C99 (стр. 412) (спасибо Johannes!), прямой декларатор определяется как:

(6.7.6) direct-declarator:  
    direct-declarator [ type-qualifier-list? * ]

Сначала я думал, что это ошибка в грамматике (список типов не должен быть необязательным). Однако, когда я попробовал это в своем ссылочном компиляторе (clang), я получил довольно неожиданную ошибку:

int array[*] = { 1, 2, 3 };
// error: star modifier used outside of function prototype

По-видимому, (в clang) это называется модификатором звезды.

Я быстро понял, что их можно использовать только в сигнатурах функций:

void foobar(int array[*])

Однако они могут использоваться только в объявлении. Попытка использовать его в определении функции также приводит к ошибке:

void foobar(int array[*]) {
    // variable length array must be bound in function definition
}

Итак, насколько я могу судить, предполагаемое поведение - использовать [*] в объявлении функции, а затем использовать определение функции в определении функции.

// public header
void foobar(int array[*]);

// private implementation
void foobar(int array[5]) {

}

Однако я никогда не видел его, и я не совсем понимаю его цель.

  • Какова его цель, почему она была добавлена?
  • Какая разница с int[]?
  • Какая разница с int *?
4b9b3361

Ответ 1

В чем его цель, почему она была добавлена?

Назначение видно, когда в качестве параметра функции используется переменная длина двух размерных массивов. Функция

int foo(int n, int m, int a[n][m])  {...}   

может быть прототипирован как любой из следующих

int foo(int , int, int [][*]);
int foo(int , int, int a[*][*]);
int foo(int , int, int (*a)[*]);
int foo(int n, int, int a[n][*]);
int foo(int , int m, int a[*][m]);
int foo(int , int m, int (*a)[m]);
int foo(int n, int m, int a[n][m]); 

В случае двумерного массива при использовании в качестве параметра функции размер второго измерения нельзя опустить. Если имя первых переменных в прототипе функции опущено, тогда было бы невозможно указать длину (второе измерение) массива. * дает понять, что длина массива будет определяться вторым параметром.

Какая разница с int[]?
Какая разница с int *?

В случае 1D массива для определения функции

int bar(int n, int a[n]} {...}  

допустим любой из следующих прототипов

int bar (int , int *);
int bar (int , int [*]);
Int bar (int , int []);
int bar (int n, int a[]);
int bar (int n, int a[n]);
int bar (int n, int [n]);   

В этом случае не требуется ни *, ни n, поскольку компилятор будет обрабатывать как int [*], так и int [n] как int *. Итак, с одномерным массивом вы не видите большой разницы.


ПРИМЕЧАНИЕ. При использовании массива переменной длины в качестве параметра функции важно указать параметр. Порядок параметров для первых четырех прототипов bar можно переключить, но в последних двух первых параметрах не должно быть самого массива.

int bar (int a[n], int n);  //Wrong. Compiler has not yet seen 'n'.

Ответ 2

В обоснованном документе C для C99 говорится

Прототип функции может иметь параметры, которые имеют типы массивов переменной длины (§6.7.5.2), используя специальный синтаксис, как в

int minimum(int, int [*][*]);

Это согласуется с другими прототипами C, где имя параметра не требуется указывать.


Какая разница с int []

Какая разница с int *.

Я просто думаю, что эти типы в прототипе функции означают "указатель", а [*] в не верхней позиции (int[*] по-прежнему равен int[], я думаю, в прототипе функции) действительно действителен и означает массив

// not recommended though: it is now unclear what the parameters
// mean to human callers!
void f(int, int [][*]);

void f(int n, int x[][n]) {
    x[1][0] = 1;
}

int main() {
   int a[2][1];
   f(1, a);
   printf("%d\n", a[1][0]);
}

Что касается этой цели, то при индексировании массива в определении функции компилятор должен знать, сколько целых чисел следующего индекса пропустить при выдаче первого индекса (x[i] пропускает i * n целые числа в f выше)). Но эта информация не нужна в объявлении прототипа без определения, поэтому его можно оставить без изменений и заменить на *.