我对以下段落如何与其后面的代码匹配感到困惑:
由于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[])
或类似的东西吗?
作为旁注,通常我发现混合数组和指针的声明相当混乱是正常的吗?
答案 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);
endptr
与argv
中main
的类型完全相同,都是指向指针的指针,但它们以不同的方式使用。 argv
指向char *
s数组中的第一个char *
;在main内部,您可以使用argv[0]
,argv[optind]
等索引,或者通过使用++argv
递增数组来逐步执行数组。
endptr
指向一个char *
。在strtol
内,增加endptr
或引用endptr[n]
以获取除{0}之外的任何n
值都没有用。
这种语义差异通过“argv是指向数组的指针”的非正式用法来表达。可能会混淆“指向数组的指针”在形式语言中的意思被忽略,因为使用简洁语言的自然本能强于坚持正式定义的愿望,这种定义告诉你不要使用最明显的简单短语,因为它是保留的对于几乎不会发生的情况。