刚刚在ISO/IEC9899中找到了一些东西我偶然发现了这个:
6.7.6类型名称
[...]
语义
2在几种情况下,有必要指定一种类型.这是使用类型名称完成的,类型名称在语法上是函数的声明或省略标识符的该类型的对象.128)3示例构造
(a) int
(b) int *
(c) int *[3]
(d) int (*)[3]
(e) int (*)[*]
(f) int *()
(g) int (*)(void)
(h) int (*const [])(unsigned int, ...)
Run Code Online (Sandbox Code Playgroud)
name分别命名为(a)int,(b)指向int的指针,(c)指向int的三个指针的数组,(d)指向三个int的数组的指针,(e)指向未指定数字的可变长度数组的指针oft,(f)没有参数规范的函数返回指向int的指针,(g)指向函数的指针,没有返回int的参数,以及(h)指向函数的未指定数量的常量指针的数组,每个指针都有一个参数具有unsigned int类型和未指定数量的其他参数,返回int.
让我最困惑的是:
(e)指向未指定数量的整数的可变长度数组的指针
我可以或多或少地了解其他人.但是指向未指定数量的'整数'的VLA的指针有什么用?
甚至还需要编译器来支持语法
int foo[*];
Run Code Online (Sandbox Code Playgroud)
?
编辑澄清
本课题主要针对"是否有必要为编译器提供支持?".虽然这篇文章ANSI-C语法 - 像[*] et alii这样的数组声明明显提高了我的知识.仍然没有答案:为什么编译器需要知道原型的参数是否是包含未知大小的地址.只是简单地做int foo[]或者它将是未指定的大小?
那么真的有必要得到支持吗?如果不是这样,那为什么标准甚至会实现这种语义呢?
为什么编译器需要知道原型的参数是否是包含未知大小的地址.简单地做int foo []或者它将是未指定的大小?
编译器不需要"知道"任何东西,它是一个工具.
之间的差异int (*)[*]和int[]大约相同,之间int (*)[5]和int[].如果你同意后一对不可互换,那么前者也不是.
在C99之前,指定未知数量T元素的数组的方法是T[].这是一个不完整的类型,这意味着你不能有一个数组T[].没有T[][].在函数声明器中,T[]与...相同T*.OTOH T[*]是一个可变长度数组,不同于未知数量元素的数组.可以具有可变大小的数组的数组,即,存在是 T[*][*].您要问的语法是支持此可变大小数组类型所必需的.幸运的是,你不是在问我们为什么需要不同的类型,因为答案真的很冗长,但这是我对它的抨击.
类型的目的是双重的.首先,对象代码生成需要类型(a++通常会生成不同的对象代码,具体取决于类型a).其次,类型检查需要类型(a++可能允许或不允许的内容取决于类型a).
这些[*]类型仅允许在不属于函数定义的函数声明符中.所以代码生成和这里没有关系.这让我们进行了类型检查.确实,
int foo(int, int (*)[*]);
int bar(int, int (*)[5]);
int main ()
{
int a;
int aa[5];
int aaa[5][5];
foo(1, &a); // incorrect, `&a` is `int*`, `int*` and `int (*)[*]` are different
bar(1, &a); // incorrect, `&a` is `int*`, `int*` and `int (*)[5]` are different
foo(5, aa); // incorrect, `aa` is `int*` (!), `int*` and `int (*)[*]` are different
bar(5, aa); // incorrect, `aa` is `int*` (!), `int*` and `int (*)[5]` are different
foo(5, &aa); // correct
bar(5, &aa); // correct
foo(5, aaa); // correct
bar(5, aaa); // correct
}
Run Code Online (Sandbox Code Playgroud)
如果我们同意哪些呼叫bar是正确的,哪些呼叫是正确的,我们也必须同意呼叫foo.
唯一剩下的问题是,为什么int foo(int m, int (*)[m]);这个目的还不够?它可能是,但C语言不强制程序员在不需要参数名称的函数声明符中命名形式参数.[*]在VLA的情况下允许这种小的自由度.
我将严格按照要求回答您的问题:
该问题主要针对"是否有必要为编译器提供支持?"
对于C99编译器,是:它是标准的一部分,因此符合C99的编译器必须支持它.什么int foo[*];是有用的问题与是否必须得到支持的问题完全正交.声称符合我测试过的C99的所有编译器都支持它(但我不确定它对它有用).
对于C11编译器,好消息!可变长度阵列已成为"条件特征".只要定义了__STDC_NO_VLA__以下内容,就可以实现符合C11标准而无需可变长度数组:
6.10.8.3条件特征宏
...
__STDC_NO_VLA__整数常量1,用于指示实现不支持可变长度数组或可变修改类型.
小智 1
如果我将一个多维数组传递给函数,并且用于表示数组给定维度中元素数量的函数参数位于数组参数本身之后,则可以使用该[*]语法。对于二维以上的数组,并且如果数组参数再次位于元素计数参数之前,则必须使用此语法,因为数组衰减仅发生一次。毕竟,您不能很好地使用or ,因为标准要求在and中,由于数组衰减为指针,只能省略 A 。如果您在函数参数中使用指针表示法,则可以使用,但这对我来说没有什么意义,特别是因为:int (*)[][]int [][][]int [A][B]int [A][B][C][D]int (*)[]
sizeof ptr[0]和sizeof *ptr都是非法的——编译器应该如何确定元素计数不确定的数组的大小?N * sizeof **ptr相反,您必须在运行时使用或找到它sizeof(int (*)[N])。这也意味着 的任何算术运算ptr(例如 的使用)++ptr都是非法的,因为它们依赖于无法计算的大小信息。类型转换可用于解决此问题,但使用具有正确类型信息的局部变量会更容易。话又说回来,为什么不直接使用[*]语法并从一开始就包含正确的类型信息呢?sizeof ptr[0][0]是非法的,但sizeof (*ptr)[0]不是——即使只是获取诸如大小之类的信息,数组索引仍然会执行,所以它就像 write 一样sizeof (*(ptr + 0))[0],这是非法的,因为您不能将算术运算应用于前面提到的不完整类型。[]可以用 代替*,用yieldint **代替int (*)[],这是不正确的,因为该子数组还没有衰减。阵列衰减仅发生一次。我注意到,[*]如果用作元素计数的参数首先出现,则语法是不必要的,这是事实,但是有人最后一次看到以下任何内容是什么时候?
void foo (int a, int b, int c, int arr[a][b][c]);
void bar (int a, int b, int c, int arr[][b][c]);
void baz (int a, int b, int c, int (*arr)[b][c]);
Run Code Online (Sandbox Code Playgroud)
所以回答你的问题:
和
[*]可能需要语法。实际上,我鼓励使用“ [*]since”,[]当需要尺寸信息时会出现问题。