指向整数数组的指针的标量初始化程序中有过多的元素

38

我正在K&R(练习5–9)中进行练习,我试图转换原始程序的二维数组

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

使用指向包含13个整数的数组的指针,如下所示:

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

但编译器输出警告:标量初始化中存在过多元素

在谷歌上搜索也没有帮助,即使K&R写道当将数组传递给函数时,

myFunction(int daytab[2][13]) {...}

等同于

myFunction(int (*daytab)[13]) {...}
3个回答

40

两者只有部分等价,其中区别在于:

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

声明一个二维数组,其中包括为数组设置空间并确保daytab引用该内存。但是:

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

在这段代码中,只声明了一个指针。因此,您尝试使用一个数组初始化程序来初始化指针,这并不像预期的那样工作。没有数组;没有为数组分配内存。相反,初始化器中的第一个数字被赋给指针daytab,并且编译器会生成一个警告以让您知道已指定了许多额外的值,它们将被忽略。由于初始化器中的第一个数字是0,因此您只是以一种相当冗长的方式设置daytabNULL

所以,如果您想要进行这种类型的初始化,请使用第一种版本--它会衰减为您在第二种版本中明确声明的相同指针类型,因此您可以以相同的方式使用它。第二个版本,使用数组指针,当您希望动态分配数组或获取对另一个已存在的数组的引用时需要使用它。

因此,您可以执行以下操作:

static char arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
static char (*ptr)[3] = NULL;

ptr = arr;

...然后可以互换使用ptrarr。或者这样:

static char (*ptr)[3] = NULL;

ptr = malloc(2 * sizeof(*ptr));

获取一个动态分配的二维数组(不是指针数组,而是真正的二维数组)。当然,在这种情况下它没有被初始化。

两种变体的“等价性”意味着,当2D数组衰减成指向其第一个元素的指针时,它会衰减为在第二个变量声明中声明的指针类型。一旦指针版本实际指向一个数组,两者就是等效的。但是,2D数组版本设置了数组的内存,而指针声明则没有… 指针可以被赋予新值(指向不同的数组),而2D数组变量不能。

在C99中,您可以这样做(如果至少不是static):

char (*daytab)[13] = (char [][13]){
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

堆栈空间?如果是静态的,则放在数据部分。 - Rag
@BrianGordon 更正了...我在输入那部分时忘记了“static”(我想...那是很久以前的事了)。 - Dmitri
在练习中,指针可以这样访问: (*(daytab + leap))[i] - Vladimir

8

@Dmitri已经解释得很好了,但我想补充一点:

static char (*daytab)[13] = { ... };

这是一个指向包含13个char元素数组的指针。编译器发出警告是因为你传入了两个数组,就像试图将两个地址分配给一个指针一样,例如char *p = {a, b}。根据你的声明,元素过多。请参考Geekforgeek的解释以了解数组指针的真正含义。

回答K&R练习有以下选项:

static char *daytab[2] = { 
    (char []) {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    (char []) {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };} 

或者选项2:
static char (*daytab)[13] = (char [][13]) { 
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };} 

选项1是两个char指针的数组。

选项2是一个数组指针。它指向由13个char元素组成的数组。就像你可以通过增加char指针来获取字符串中的下一个字母一样,你也可以增加这个数组指针来获取下一个由13个char组成的数组。


static char (*daytab)[13] 不是指向 char 的指针,而是指向 *13 元素数组的指针,可以用来指向实际的二维数组。你的例子中 static char *daytab[2] 是不同的,其中 daytab 是指向 char 指针的数组,每个指向一个一维的 char 数组。在你的例子中,daytab 不是一个指针,它所引用的数组包含指针而不是 char - Dmitri
很好的澄清@Dmitri。我稍微更新了我的答案。 - khlau

0

我自己在K&R中解决了这个问题,也许我可以补充一下已经给出的非常好的答案。这是一个很好的练习,可以摆脱使用二维数组并转向使用指针数组。请注意,在本书的这个阶段,我们还没有介绍malloc。因此,一种方法是预先设置月份数组,然后是指向这些数组的指针数组:

char y0[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char y1[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *daytab[] = {y0, y1};

回想一下,数组的名称是指向第一个元素的指针。现在你真正拥有一个指向两个包含13个int元素的数组的指针数组。


请记住,C语言实际上没有二维数组。您可以创建一个由一维数组组成的数组,这与二维数组差不多。只是它是n[x][y]而不是n[x,y]。 - Alan Corey

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