ANSI C грамматика от - ссылка дает мне следующие правила для объявлений массива:
(1) | direct_declarator '[' type_qualifier_list assignment_expression ']'
(2) | direct_declarator '[' type_qualifier_list ']'
(3) | direct_declarator '[' assignment_expression ']'
(4) | direct_declarator '[' STATIC type_qualifier_list assignment_expression ']'
(5) | direct_declarator '[' type_qualifier_list STATIC assignment_expression ']'
(6) | direct_declarator '[' type_qualifier_list '*' ']'
(7) | direct_declarator '[' '*' ']'
(8) | direct_declarator '[' ']'
Теперь у меня есть некоторые вопросы о них:
Что является различием между следующими двумя прототипами функции:
void foo(int [*]);
и
void foo(int []);
Спасибо.
Вы не можете использовать квалификаторы типов или static
в части размера объявления массива в C89/90. Эти особенности характерны для C99.
static
в объявлении массива говорит компилятору, что вы обещаете, что указанное количество элементов всегда будет присутствовать в массиве, переданном в качестве фактического аргумента. Это может помочь компиляторам генерировать более эффективный код. Если вы нарушите свое обещание в реальном коде (т.е. передадите меньший массив), поведение будет неопределенным. Например,
void foo(int a[static 3]) {
...
}
int main() {
int a[4], b[2];
foo(a); /* OK */
foo(b); /* Undefined behavior */
}
*
в части size объявления массива используется только в объявлениях прототипов функций. Он указывает, что массив имеет переменную длину (VLA). Например, в определении функции вы можете использовать VLA с конкретным run-time размером
void foo(int n, int a[n]) /* `a` is VLA because `n` is not a constant */
{
...
}
При объявлении прототипа вы можете сделать то же самое
void foo(int n, int a[n]); /* `a` is VLA because `n` is not a constant */
но если вы не указываете имена параметров (что нормально в прототипе), вы, конечно, не можете использовать n
в качестве размера массива. Тем не менее, если вам все же нужно сообщить компилятору, что массив будет VLA, вы можете использовать *
для этой цели
void foo(int, int a[*]); /* `a` is VLA because size is `*` */
Обратите внимание, что пример с одномерным массивом не очень удачный. Даже если опустить *
и объявить приведенную выше функцию как
void foo(int, int a[]);
то код все равно будет работать нормально, поскольку в объявлениях параметров функций тип массива все равно неявно заменяется на тип указателя. Но как только вы начинаете использовать многомерные массивы, правильное использование *
становится важным. Например, если функция определена как
void bar(int n, int m[n][n]) { /* 2D VLA */
...
}
прототип может выглядеть следующим образом
void bar(int n, int m[n][n]); /* 2D VLA */
или как
void bar(int, int m[*][*]); /* 2d VLA */
В последнем случае первый *
можно опустить (из-за замены массива на указатель), но не второй *
.
Надеюсь, вы не пытаетесь изучить грамматику C по спецификации yacc !? Отправленная вами ссылка, похоже, основана на проекте стандарта ISO C99 . Соответствующий раздел - 6.7.5.2. Формулировка непонятна (но, возможно, меньше, чем синтаксис yacc!)
Мой K&R2nd (который охватывает и включает стандарт ANSI), похоже, ничего не говорит о [*]
ни в тексте, ни в самом стандарте. Я также не могу заставить официальную грамматику в стандарте принять этот синтаксис.
Он может быть связан с K&R c (хотя я не припоминаю этого), может быть обычным расширением, или был предложением, которое в итоге не вошло в стандарт.
Я бы предположил, что это делает размерность массива явно неуказанной. Но я просто предполагаю.
Хм... gcc принимает
#include <stdio.h>
void f(int s, int a[*]);
int main(void){
int a[2] = {0};
f(2,a);
return 0;
}
void f(int s, int a[]){
int i;
for (i=0; i<s; ++i){
printf("%d\n",a[i]);
}
}
в режиме ansi, c89 и c99; не выдавая предупреждений даже с -Wall
. Обратите внимание, что ему не понравился синтаксис [*]
в определении функции. Добавление -pedantic
заставило его жаловаться на синтаксис [*]
в режимах c89 и ansi, но он продолжал принимать его в режиме c99.