argv指向指针数组的指针

时间:2013-06-22 20:10:09

标签: c arrays pointers argv kernighan-and-ritchie

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

  

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

#include <stdio.h>
/* 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[])或类似的东西吗?

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

6 个答案:

答案 0 :(得分:10)

诸如“指向数组的指针”或“指向数组”之类的术语通常在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指向一个指针数组是正确的。

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

答案 1 :(得分:4)

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指针数组的第一个元素,但它本身不是数组类型,并且它(正式)指向整个数组。但我们知道数组就在那里,我们可以将其编入索引以获取其他元素。

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

答案 2 :(得分:2)

数组指针等价事物仅对函数参数仅包含 ,因此虽然void fn(const char* argv[])void fn(const char** argv)是等价的,但它不会当你想要传递给函数的变量时,t保持为真。

考虑

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);

答案 3 :(得分:0)

Since argv is a pointer to an array of pointers

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

答案 4 :(得分:0)

  

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

不,甚至不接近。

  

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

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

答案 5 :(得分:0)

“指向数组的第一个元素的指针”是一个常见的结构。每个字符串函数都使用它,包括输入和输出字符串的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]以获取除{0}之外的任何n值都没有用。

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