0

我正在阅读这个问题的答案为什么在声明多维数组时允许省略第一个维度,而不是其他维度? 以及其他一些问题,我知道我们可以省略第一个维度,但其他维度不可避免地要指定。
令我惊讶的是,以下代码执行得非常好:

#include<stdio.h>
void f1(int (*p)[]){
    //SOMETHING
}
int main()
{
    int a[3][3]={1,2,3,4,5,6,7,8,9};
    f1(a);
}

但是,如果我使用指针p打印一些值,例如

printf("%d",p[0][1]);

它给出了一条错误消息:

error: invalid use of array with unspecified bounds

为什么 C 允许这样的声明?
如果非常确定它会在使用指针时抛出错误,那么为什么它在声明本身时不抛出错误?为什么要等待指针使用抛出错误?
允许这样的声明有什么具体原因吗?

4

2 回答 2

4

表达式p[0][1]包含p[0]. p[0]被定义为等价于*(p+0)。这使用了指针和整数的相加。

有一个规则,用指针加法需要一个指向完整对象类型的指针。这是因为计算地址必须更改的字节数通常需要知道所指向对象的大小。添加零时不会,但规则中没有例外。

的类型p是“指向未知数量的数组的指针int。因为数组中的元素个数未知,所以类型不完整。所以不允许添加。

有趣的是,(*p)[1]是允许的,即使它指的是同一件事p[0][1]。这是允许的,因为计算*p不需要添加指针。p指向一个数组,所以我们知道该数组从哪里开始,即使我们不知道它的大小。

需要知道指针运算的对象大小是数组元素必须具有完整类型的原因(因此,如果这些元素本身是数组,则必须给出它们的长度,以使它们完整,因此数组的所有内部维度必须为人所知)。但是允许指针指向不完整的类型,因此,当指针指向数组时,不需要知道数组维数。

于 2020-08-28T03:01:17.870 回答
1

我知道我们可以省略第一个维度,但不可避免地要指定其他维度。

是的。声明数组时,元素类型必须是完整类型。这是标准第 6.7.6.2/1 段中指定的正式约束,因此符合标准的编译器必须诊断违规。

但那又怎样?您在询问p...中的参数声明

void f1(int (*p)[]){

...具有指针类型。具体来说,它是一个指向未知数量整数数组的指针。该数组类型仅省略第一个维度,如您所知,这是允许的,尽管它使该类型不完整。允许指向不完整类型的指针(并且它们本身就是完整类型),其中 typevoid *是典型的子类型。此外, typeint(*)[]与您传递的参数的类型兼容,即int(*)[3](not int[3][3])。

为什么 C 允许这样的声明?

为什么不应该呢?我的意思是,不完整的类型有点奇怪,但它们有一个有用的目的。您提供的参数声明与 C 的要求一致。

如果非常确定它会在使用指针时抛出错误,那么为什么它在声明本身时不抛出错误?为什么要等待指针使用抛出错误?

因为只有指针的某些用途是错误的。例如,您可以将其转换为整数或其他指针类型,或将其分配给兼容类型的变量。事实上,尽管 C 没有定义解引用指向其他不完整类型的指针的行为,但它确实允许解引用指向不完整数组类型的指针。

允许这样的声明有什么具体原因吗?

一致性?用处?您的问题似乎基于这样一种信念,即允许它是一致的,无用的,或两者兼而有之,但两者都不是。C 有一个不完整类型的概念。它允许指向不完整类型的指针,这对语言非常重要,并且在这方面它不区分不完整类型。C 也没有针对指向不完整类型的指针作为函数参数的类型制定特殊规则。

于 2020-08-28T03:45:21.950 回答