让我先说点题外话:
无论如何,看起来您正在查看本章中的额外学分练习。
- 另一个旁白——我不认为这是一个特别明智的学习练习(另一个答案指出这个问题没有意义),所以这个讨论会变得有点复杂。相反,我会推荐K&R第 5 章中的练习。
首先,我们需要了解指针与数组不同。我在这里的另一个答案中对此进行了扩展,我将从C 常见问题解答中借用相同的图表。以下是我们声明数组或指针时内存中发生的情况:
char a[] = "hello"; // array
+---+---+---+---+---+---+
a: | h | e | l | l | o |\0 |
+---+---+---+---+---+---+
char *p = "world"; // pointer
+-----+ +---+---+---+---+---+---+
p: | *======> | w | o | r | l | d |\0 |
+-----+ +---+---+---+---+---+---+
所以,在书中的代码中,当我们说:
int ages[] = {23, 43, 12, 89, 2};
我们得到:
+----+----+----+----+---+
ages: | 23 | 43 | 12 | 89 | 2 |
+----+----+----+----+---+
为了解释的目的,我将使用非法声明 - 如果我们可以说:
int *ages = {23, 43, 12, 89, 2}; // The C grammar prohibits initialised array
// declarations being assigned to pointers,
// but I'll get to that
这将导致:
+---+ +----+----+----+----+---+
ages: | *=====> | 23 | 43 | 12 | 89 | 2 |
+---+ +----+----+----+----+---+
稍后可以以相同的方式访问这两者 - 第一个元素“23”可以通过 访问ages[0]
,无论它是数组还是指针。到目前为止,一切都很好。
但是,当我们想要获得计数时,我们会遇到问题。C 不知道数组有多大——它只知道它知道的变量有多大(以字节为单位)。这意味着,使用数组,您可以通过以下方式计算出大小:
int count = sizeof(ages) / sizeof(int);
或者,更安全地:
int count = sizeof(ages) / sizeof(ages[0]);
在数组的情况下,这表示:
int count = the number of bytes in (an array of 6 integers) /
the number of bytes in (an integer)
它正确地给出了数组的长度。但是,对于指针情况,它将显示为:
int count = the number of bytes in (**a pointer**) /
the number of bytes in (an integer)
这几乎肯定与数组的长度不同。在使用指向数组的指针的地方,我们需要使用另一种方法来计算数组的长度。在 C 中,以下两种情况都是正常的:
记住有多少元素:
int *ages = {23, 43, 12, 89, 2}; // Remember you can't actually
// assign like this, see below
int ages_length = 5;
for (i = 0 ; i < ages_length; i++) {
或者,保留一个标记值(永远不会作为数组中的实际值出现)来指示数组的结尾:
int *ages = {23, 43, 12, 89, 2, -1}; // Remember you can't actually
// assign like this, see below
for (i = 0; ages[i] != -1; i++) {
(这就是字符串的工作方式,使用特殊的 NUL 值 '\0' 来指示字符串的结尾)
现在,请记住我说过你实际上不能写:
int *ages = {23, 43, 12, 89, 2, -1}; // Illegal
这是因为编译器不允许您将隐式数组分配给指针。如果你真的想,你可以写:
int *ages = (int *) (int []) {23, 43, 12, 89, 2, -1}; // Horrible style
但是不要,因为它读起来非常不愉快。出于本练习的目的,我可能会写:
int ages_array[] = {23, 43, 12, 89, 2, -1};
int *ages_pointer = ages_array;
请注意,编译器正在将数组名称“衰减”为指向它的第一个元素的指针 - 就好像你写过:
int ages_array[] = {23, 43, 12, 89, 2, -1};
int *ages_pointer = &(ages_array[0]);
但是 - 您也可以动态分配数组。对于这个示例代码,它会变得很罗嗦,但我们可以将其作为一个学习练习。而不是写:
int ages[] = {23, 43, 12, 89, 2};
我们可以使用 malloc 分配内存:
int *ages = malloc(sizeof(int) * 5); // create enough space for 5 integers
if (ages == NULL) {
/* we're out of memory, print an error and exit */
}
ages[0] = 23;
ages[1] = 43;
ages[2] = 12;
ages[3] = 89;
ages[4] = 2;
ages
请注意,当我们完成内存时,我们需要释放:
free(ages);
另请注意,有几种方法可以编写 malloc 调用:
int *ages = malloc(sizeof(int) * 5);
这对于初学者来说更容易阅读,但通常被认为是不好的风格,因为如果您更改ages
. 相反,您可以编写以下任一项:
int *ages = malloc(sizeof(ages[0]) * 5);
int *ages = malloc(sizeof(*ages) * 5);
这些陈述是等效的-您选择的内容是个人风格的问题。我更喜欢第一个。
最后一件事 - 如果我们将代码更改为使用数组,您可能会考虑更改:
int main(int argc, char *argv[]) {
但是,你不需要。原因有点微妙。首先,这个声明:
char *argv[]
说“有一个指向字符的数组,称为 argv”。但是,编译器将函数参数中的数组视为指向数组第一个元素的指针,因此如果您编写:
int main(int argc, char *argv[]) {
编译器实际上会看到:
int main(int argc, char **argv)
这也是您可以省略用作函数参数的多维数组的第一维长度的原因 - 编译器不会看到它。