Что означает "[*]" (звездный модификатор) в 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 *
?
Ответы
Ответ 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
выше)). Но эта информация не нужна в объявлении прототипа без определения, поэтому его можно оставить без изменений и заменить на *
.