“[*]”(星号修饰符)在 C 中是啥意思? [复制]

Posted

技术标签:

【中文标题】“[*]”(星号修饰符)在 C 中是啥意思? [复制]【英文标题】:What does "[*]" (star modifier) mean in C? [duplicate]“[*]”(星号修饰符)在 C 中是什么意思? [复制] 【发布时间】:2016-12-11 01:15:07 【问题描述】:

在尝试实现 C11 解析器(用于教育目的)时,我发现在 C11 (p. 470) 和 C99 (p. 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 中)这被称为 star 修饰符

我很快了解到它们只能用于函数签名:

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 *有什么区别?

【问题讨论】:

自 C99 以来一直使用 C。我相信它的意思是“这里 VLA 号码的占位符”。 int[][] 无效,但 int[][*] 有效(在非定义函数原型中)。 你说得对,我忘了提,但我搜索了从 clang 得到的错误,它们对应于他们的 VLA 单元测试套件。不过,我想知道他们的目的。谢谢! 22 次浏览,这已经是“c 星修饰符”的第一个谷歌搜索结果。您发现了一个不起眼的功能。 Clang 什么时候成为“参考编译器”? @user2357112 我不认为他的意思是在任何特定的能力,只是他个人用来检查他对标准的解释是否合理的编译器。 【参考方案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 * 有什么区别?

如果是一维数组,对于函数定义

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

【讨论】:

这直接回答了问题,而不是仅仅引用一个标准。 标准引用直接回答了问题,而且很权威,因为它来自标准。标准报价没有错。 (想想看,那句话来自基本原理文档,而不是标准。) @user2357112 有另一个答案不是我直接引用标准的。我猜阿尔维茨对此有意见。正如您正确指出的那样,我的没有引用 C 标准 int bar (int [*], int); 是正确的。 引用标准并没有什么错误本身。事实上,这通常是一个好主意。当答案仅包含标准的引用而没有任何解释时,问题就来了。一般来说,如果有人能够阅读并理解该标准的确切含义,他们就不会在 Stack Overflow 上提出有关它的问题。所以一个好的答案(对一个相关的问题)确实需要两者兼而有之。如果您必须选择其中一个,我必须同意 alvits 的观点,并说解释优于引用。【参考方案2】:

C99 的 C 原理文档说

函数原型可以使用具有可变长度数组类型(第 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中的整数)。但在非定义原型声明中不需要此信息,因此可以将其省略并替换为*

【讨论】:

你的函数原型应该是void f(int, int x[][*])。当您给第一个参数命名时,就不需要*。此功能的全部意义在于您可以使用不命名参数的老式函数原型。 @user3386109 嗯,C89 中不允许命名参数吗?我以为这总是被允许的?为什么省略名字是“老式的”?但是,确实,如果您仍然命名参数,动机就不那么高了

以上是关于“[*]”(星号修饰符)在 C 中是啥意思? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

static在c语言中是啥意思

C#中的??是啥意思

在C#中internal关键字是啥意思?

C语言里%zd是啥意思?

C语言当中结构句后面一个星号,是啥意思?

C语言当中结构句后面一个星号,是啥意思?