Грамматика ANSI-C - объявления массива как [*] и alii

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

Теперь у меня есть некоторые вопросы о них:

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

    void foo(int [*]); и

    void foo(int []);

Спасибо.

10
задан James McNellis 19 June 2010 в 22:36
поделиться

3 ответа

Вы не можете использовать квалификаторы типов или 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 */

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

15
ответ дан 3 December 2019 в 21:59
поделиться

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

1
ответ дан 3 December 2019 в 21:59
поделиться

Мой 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.

0
ответ дан 3 December 2019 в 21:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: