argv指向指针数组的指针

我很困惑以下段落如何与其后面的代码匹配:

由于argv是指向指针数组的指针 ,因此我们可以操作指针而不是索引数组。 下一个变体基于递增argv,它是指向char的指针,而argc倒计时:

#include  /* echo command-line arguments; 2nd version */ main(int argc, char *argv[]) { while (--argc > 0) printf("%s%s", *++argv, (argc > 1) ? " " : ""); printf("\n"); return 0; } 

不是char *argv[]只是一个指针数组? 指向数组的指针不会写为char *(*argv[])或类似的东西吗?

作为旁注,通常我发现混合数组和指针的声明相当混乱,这是正常的吗?

术语“指向数组的指针”或“指向数组”通常在C术语中相当宽松地处理。 他们至少可以有两种不同的东西。

在这个术语最严格和最迂腐的意义上,“指向数组的指针”必须用“指向数组的指针”类型声明,如

 int a[10]; int (*p)[10] = &a; 

在上面的例子中, p被声明为一个指向10个int数组的指针,它实际上被初始化为指向这样一个数组。

但是,这个术语也常用于其不太正式的含义。 在这个例子中

 int a[10]; int *p = &a; 

p被声明为仅仅指向int指针。 它被初始化为指向数组a的第一个元素。 您经常可以听到并看到人们说在这种情况下p也“指向int的数组”,即使这种情况在语义上与前一个不同。 在这种情况下,“指向数组”意味着“通过指针算术提供对数组元素的访问”,如p[5]*(p + 3)

这正是短语“… argv指向指针数组的指针……”的含义。 argvmain参数列表中的声明等同于char **argv ,这意味着argv实际上是指向char *指针的指针。 但是因为它物理上指向某些char *指针数组的第一个元素(由调用代码维护),所以半非正式地说argv指向一个指针数组是正确的。

这正是你引用的文字的含义。

在C函数声称接受数组的地方,严格来说它们接受指针。 该语言不区分void fn(int *foo) {}void fn(int foo[]) 。 它甚至不关心你是否有void fn(int foo[100])然后传递一个int [10]数组。

 int main(int argc, char *argv[]) 

是相同的

 int main(int argc, char **argv) 

因此, argv指向char指针数组的第一个元素,但它本身不是一个数组类型,并且它(正式)不指向整个数组。 但是我们知道数组就在那里,我们可以索引它以获得其他元素。

在更复杂的情况下,比如接受多维数组,只有第一个[]回退到指针(并且可以保持未分级)。 其他的仍然是指向的类型的一部分,它们对指针算术有影响。

数组指针等价的东西只对函数参数才成立,所以虽然void fn(const char* argv[])void fn(const char** argv)是等价的,但当它涉及到时,它不成立您可能想要传递给函数的变量。

考虑

 void fn(const char** argv) { ... } int main(int argc, const char* argv[]) { fn(argv); // acceptable. const char* meats[] = { "Chicken", "Cow", "Pizza" }; // "meats" is an array of const char* pointers, just like argv, so fn(meats); // acceptable. const char** meatPtr = meats; fn(meatPtr); // because the previous call actually cast to this,. // an array of character arrays. const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" }; fn(vegetables); // does not compile. return 0; } 

“vegetables”不是指向指针的指针,它直接指向3 * 10连续字符序列中的第一个字符。 在上面替换fn(蔬菜)得到

 int main(int argc, const char* argv[]) { // an array of character arrays. const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" }; printf("*vegetables = %c\n", *(const char*)vegetables); return 0; } 

输出是“A”:蔬菜本身直接指向 – 没有间接 – 指向字符,而不指向中间指针。

蔬菜分配基本上是一个捷径:

 const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0"; vegetables = __vegetablesPtr; 

 const char* roni = vegetables[2]; 

翻译成

 const char* roni = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2); 

Since argv is a pointer to an array of pointers

这是错的。 argv是一个指针数组。

由于argv是指向指针数组的指针,

不,甚至没有关闭。

不是char *argv[]只是一个指针数组?

不,它是指针的指针。

“指向数组的第一个元素的指针”是一个常见的结构。 每个字符串函数都使用它,包括输入和输出字符串的stdio函数。 main用于argv。

“指向数组的指针”是一种罕见的结构。 我在C标准库或POSIX中找不到它的任何用途。 grepping我本地安装的所有头文件(对于'([^)]*\*[^)]) *\[' )我找到了2个指向数组的合法实例,一个在libjpeg中,一个在gtk中。 (两者都是结构成员,而不是函数参数,但这不是重点。)

因此,如果我们坚持官方语言,我们有一个罕见的东西,一个简短的名字和一个类似但更常见的东西,长名称。 这与人类语言自然需要工作的方式相反,所以存在紧张,除了最正式的情况之外,通过使用短名称“错误”来解决。

我们不只是说“指向指针”的原因是指针的另一个常见用法是函数参数,其中参数指向不是数组成员的单个对象。 例如,在

 long strtol(const char *nptr, char **endptr, int base); 

endptrargvmain类型完全相同,都是指向指针的指针,但它们以不同的方式使用。 argv指向char * s数组中的第一个char * ; 在main中你应该使用它与argv[0]argv[optind]等索引,或者通过使用++argv递增它来逐步执行数组。

endptr指向一个char * 。 在strtol内部,增加endptr或引用endptr[n]对于除零以外的任何n值都没有用。

这种语义差异表现为“argv是指向数组的指针”的非正式用法。 可能会混淆“forms指针”在forms语言中的意思被忽略,因为使用简洁语言的自然本能强于坚持正式定义的愿望,这种定义告诉你不要使用最明显的简单短语,因为它是保留的对于几乎不会发生的情况。