ANSI-C грамматика - объявления массива, такие как [*] и др. - PullRequest
10 голосов
/ 22 февраля 2010

Грамматика ANSI C от -link- дает мне следующие правила для объявлений массива:

 (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 '[' ']'

Теперь у меня есть несколько вопросов по этому поводу:

  • Могу ли я использовать (1) - (6) кроме (3) только в C99?
  • Для чего (4) и (5)? Ключевое слово "static" смущает меня.
  • Где использовать (6)?
  • В чем разница между следующими двумя прототипами функций:

    void foo(int [*]); и

    void foo(int []);

Спасибо.

Ответы [ 3 ]

15 голосов
/ 22 февраля 2010

Нельзя использовать квалификаторы типа или 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 */
}

Часть размера * объявления массива используется только в объявлениях прототипов функций. Это указывает на то, что массив имеет переменную длину (VLA). Например, в определении функции вы можете использовать VLA с конкретным размером времени выполнения

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 */

В последнем случае первый * может быть опущен (из-за замены массива на указатель), но не второй *.

1 голос
/ 22 февраля 2010

Я надеюсь, что вы не пытаетесь изучать грамматику C из спецификации yacc !? Похоже, что опубликованная вами ссылка основана на проекте ISO C99 . Соответствующий раздел - 6.7.5.2. Формулировка таинственная (но, возможно, меньше, чем синтаксис yacc!)

0 голосов
/ 22 февраля 2010

My 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.

...