`void foo(int a[static 0]);` 这个语法是合法的吗?

4
以下函数是否严格符合C99标准?
void foo(int a[static 0]) {
  (void)a;
}

GCC和Clang都会发出关于使用零大小数组的警告,但我认为这个警告是没有道理的。据我所知,6.7.6.3p7中提到static关键字表示指针a应该指向至少零个元素,这个要求对于任何具有确定值的指针来说都是显而易见的。
此外,在以下情况下可以调用这样的函数吗:
int i;
foo(NULL);
foo(&i);
foo(&i + 1);

1
您不允许声明零大小的数组。您可以同样有意义地使用1 - Jonathan Leffler
1
@JonathanLeffler:foo(&i + 1)static 0 的情况下是有效的,但与 static 1foo 声明不匹配。 - chqrlie
我认为对于类似的语言解析问题,我们过去已经推断出,在将数组传递给函数之前,编译器首先会检查它是否是一个有效的数组,然后再将其调整为指向第一个元素的指针。这与为什么int arr[][]不是一个有效的函数参数的原因相同。虽然我不记得具体是哪一章“否定”了数组衰减的规则。 - Lundin
2个回答

5
据我所知,6.7.6.3p7表明static关键字表示指针a应该至少指向零个元素...
6.7.6.3 7不是唯一适用的规则。
正如您所知,声明为数组的参数会自动转换为指针。但在进行调整之前,它将被声明为数组。数组声明符的规则之一是6.7.6.2 1中的约束:
另外,在可选类型限定符和关键字static的情况下,[]可以限定一个表达式或者*。如果它们限定了一个表达式(指定了数组的大小),那么该表达式必须具有整数类型。如果该表达式是一个常量表达式,它的值必须大于零...
即使满足了6.7.6.3 7,违反了6.7.6.2 1仍然属于约束违规。
...指向至少零个元素,这个要求对于任何具有确定值的指针都是显然满足的。

不,不是这样的。首先,空指针不指向至少零个元素,因为它没有指向任何内容。

其次,指针不仅仅要求指向至少零个元素,而是要求具体指向至少零个元素的数组的第一个元素。任何包含至少零个元素的数组都至少有一个元素,因为6.2.5 20将数组类型定义为“具有特定成员对象类型的连续分配的非空对象集合”。所以,仅仅是一个数组的事实就意味着它至少有一个元素。

因此,即使声明为int a[static 0]a必须指向至少一个int


如何指定数组切片的起始和结束指针必须非空?例如:void T_sort(T start[static 0], T end[static 0]),其中两个指针都可以指向数组末尾的元素,但不能为null。 - chqrlie
@chqrlie:C标准没有提供一种指定所有内容的方法。 - Eric Postpischil
@chqrlie static 1可以使用。带有static 1的数组参数的指针可能会指向数组之外的1个项目。实际上,C语言中的任何标量变量都被视为大小为1的数组,用于确定指针算术是否越界。 - Lundin
1
@Lundin:在数组声明符中,static 1 表示结果指针必须指向至少有一个元素的数组的第一个元素。指向数组末尾之外(不巧合地没有其他对象存在)的指针并不指向至少有一个元素的数组的第一个元素。 - Eric Postpischil
然而,如果指针对表示非空切片的边界,则将下限声明为T start[static 1]是合理的。但我没有看到将上限声明为T end[]或等效方式的替代方案。因此,如果想要编译器帮助检测无效使用,那么采用起始位置+长度的风格可能更容易实现这一目标。 - John Bollinger
或者,如果愿意改变参数的含义,那么可以使用 包容型 的端点代替独占型端点。当您需要切片为非空时,这不会产生特别的问题,然后您可以为起始和结束都声明 [静态 1] 的维度。 - John Bollinger

5

这是不符合规范的,因为数组不能有大小为0。因此也不是“至少为0”。

C17 6.7.6.2 强调如下:

约束条件

除了可选的类型限定符和关键字 static 外,[ 和 ] 可以界定一个表达式或者 *。如果它们界定一个表达式(指定数组的大小),那么该表达式必须具有整数类型。如果该表达式是一个常量表达式,则其值必须大于零。

(在 C99 中,相同的文本可以在 6.7.5.2 下找到)


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