ANSI-C语法 - 数组声明中的[*]等

10

我从 -link- 获取的 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 '[' ']'

现在我有一些关于这些的问题:

  • 我可以只在C99中使用 (1) - (6) 中除了 (3) 吗?
  • (4) 和 (5) 是用来做什么的? 关键字 'static' 让我感到困惑。
  • 何时使用 (6)?
  • 以下两个函数原型有什么区别:

    void foo(int [*]);

    void foo(int []);

谢谢。

3个回答

15

在 C89/90 中,你不能在数组声明的大小部分使用类型限定符或 static 关键字,这些特性是只属于 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 `*` */

请注意,使用1D数组的示例并不是一个好的例子。即使您省略了*并将上述函数声明为

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 */
在后一种情况下,第一个*可以省略(因为数组被替换为指针),但第二个*不能省略。

我想这真正重要的地方是当你执行 f(int, int, int a[*][*]) 或类似操作时... - dmckee --- ex-moderator kitten
@dmckee: 我刚刚把那个加到我的回答里 :) - AnT stands with Russia
这里有很多人都在思考同样的问题。伟大的思想和智慧汇聚于此... - dmckee --- ex-moderator kitten

1

希望你不是想从yacc规范中学习C语法吧!你发的链接似乎基于ISO C99草案。相关章节是6.7.5.2。措辞很古怪(但也许比yacc语法稍微好懂一些!)


@tur1ng:仅仅你说过“来自-link-的ANSI C语法给数组声明的以下规则”。我建议这些规则是为了供yacc使用而不是人类使用,并且ISO标准旨在供人类使用(并且是yacc的源文件)。我确实说了“C语法”而不仅仅是“C”; 您可以学习C以达到工作水平,而无需了解语法的复杂知识。也许您正在编写编译器或静态分析器? - Clifford
哦,抱歉,我读得太快了。是的,这是为静态分析器而设计的。 - tur1ng

0

我的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模式。


-Wall不会针对在C89中被视为GNU扩展的所有语法发出警告。我认为你还需要-pedantic参数来实现。K&R第2版是针对ANSI C89(现在的ISO C90)标准编写的,因此不包括C99语法。 C99添加了可变长度数组和其他与数组相关的语法。 - Clifford
关于不喜欢函数定义中的[],标准规定[]“是一个未指定大小的可变长度数组类型,只能在函数原型作用域的声明中使用”。 - Clifford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接