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
指向指针数组的指针……”的含义。 argv
在main
参数列表中的声明等同于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);
endptr
与argv
在main
类型完全相同,都是指向指针的指针,但它们以不同的方式使用。 argv
指向char *
s数组中的第一个char *
; 在main中你应该使用它与argv[0]
, argv[optind]
等索引,或者通过使用++argv
递增它来逐步执行数组。
endptr
指向一个char *
。 在strtol
内部,增加endptr
或引用endptr[n]
对于除零以外的任何n
值都没有用。
这种语义差异表现为“argv是指向数组的指针”的非正式用法。 可能会混淆“forms指针”在forms语言中的意思被忽略,因为使用简洁语言的自然本能强于坚持正式定义的愿望,这种定义告诉你不要使用最明显的简单短语,因为它是保留的对于几乎不会发生的情况。