C 指向指针数组的argv指针

C 指向指针数组的argv指针,c,arrays,pointers,argv,kernighan-and-ritchie,C,Arrays,Pointers,Argv,Kernighan And Ritchie,我不明白下面这段话与后面的代码是如何匹配的: 由于argv是指向指针数组的指针,因此我们可以操作 指针而不是索引数组。下一个变体基于 递增argv,它是指向char的指针,而argc 倒计时: #包括 /*回显命令行参数;第二版*/ main(int argc,char*argv[]) { 而(--argc>0) printf(“%s%s”,*++argv,(argc>1)?:”; printf(“\n”); 返回0; } char*argv[]不就是一个指针数组吗?指向指针数组的指针不应该写

我不明白下面这段话与后面的代码是如何匹配的:

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

#包括
/*回显命令行参数;第二版*/
main(int argc,char*argv[])
{
而(--argc>0)
printf(“%s%s”,*++argv,(argc>1)?:”;
printf(“\n”);
返回0;
}
char*argv[]
不就是一个指针数组吗?指向指针数组的指针不应该写成
char*(*argv[])
或类似的东西吗


作为旁注,一般来说,我会发现混合数组和指针的声明非常混乱,这正常吗?

,因为argv是指向指针数组的指针

这是错误的
argv
是指针数组

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

不,甚至不接近

char*argv[]
不就是一个指针数组吗

不,它是指向指针的指针。

像“指向数组的指针”或“指向数组”这样的术语在C术语中通常处理得相当松散。它们至少有两种不同的含义

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

int a[10];
int (*p)[10] = &a;
在上面的示例中,
p
被声明为指向10
int
s数组的指针,它实际上被初始化为指向这样一个数组

然而,这个词也经常被使用,因为它的含义不太正式。在这个例子中

int a[10];
int *p = &a;
p
仅被声明为指向
int
的指针。它被初始化为指向数组
a
的第一个元素。你经常可以听到和看到人们说,在这种情况下,
p
也“指向
int
s的数组”,尽管这种情况在语义上与前一种情况不同。在本例中,“指向数组”表示“通过指针算法提供对数组元素的访问”,如
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
指针数组的第一个元素,但它本身不是数组类型,并且(形式上)不指向整个数组。但我们知道数组就在那里,我们可以索引到它中以获得其他元素

在更复杂的情况下,如接受多维数组,只有第一个
[]
返回到指针(可以不调整大小)。其他元素仍然是指向的类型的一部分,它们对指针算法有影响。

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

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

因此,如果我们坚持使用官方语言,我们会有一个罕见的短名称,还有一个类似但更常见的长名称。这与人类语言自然希望的工作方式相反,因此会出现紧张,除了最正式的情况外,在所有情况下都会通过“错误地”使用短名称来解决

我们之所以不说“指针指向指针”,是因为指针作为函数参数还有另一种常见用法,即参数指向一个不是数组成员的对象

long strtol(const char *nptr, char **endptr, int base);
endptr
char*
数组中的
argv
类型完全相同,两者都是指向指针的指针,但它们的使用方式不同。
argv
指向
char*
数组中的第一个
char*
字符;在main中,您需要将其与
argv[0]
argv[optind]
等,或通过使用
++argv
递增数组来逐步遍历数组

endptr
指向一个
char*
。在
strtol
内部,增加
endptr
或参考
endptr[n]
中除零以外的任何
n
值都是无用的

语义上的差异表现为“argv是指向数组的指针”的非正式用法。可能与“指向数组的指针”混淆形式语言中的手段被忽略了,因为使用简洁语言的本能比坚持形式定义的愿望更强烈,形式定义告诉你不要使用最明显的简单短语,因为它只适用于几乎永远不会发生的情况
long strtol(const char *nptr, char **endptr, int base);
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;
}
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;
}
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);