I am confused as to how the following passage matches up with the code that follows it:
我很困惑,下面这段文字是如何与后面的代码相匹配的:
Since argv is a pointer to an array of pointers, we can manipulate the pointer rather than index the array. This next variant is based on incrementing argv, which is a pointer to pointer to char, while argc is counted down:
因为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;
}
Isn't char *argv[]
just an array of pointers? Wouldn't a pointer to an array of pointers be written as char *(*argv[])
or something similar?
char *argv[]难道不只是一个指针数组吗?指向指针数组的指针不是应该写成char *(*argv[])还是类似的?
As a side note, is it normal that in general I find declarations that mix arrays and pointers rather confusing?
顺便说一下,一般情况下,我发现混合数组和指针的声明是很混乱的,这是正常的吗?
6 个解决方案
#1
7
The terms "pointer to array" or "to point to an array" are often treated rather loosely in C terminology. They can have at least two different things.
在C术语中,“指向数组的指针”或“指向数组”的术语通常处理得比较松散。他们至少可以有两种不同的东西。
In the most strict and pedantic sense of the term, a "pointer to array" has to be declared with "pointer to array" type, as in
在最严格和迂腐的意义上,“指向数组的指针”必须声明为“指向数组的指针”类型,如In
int a[10];
int (*p)[10] = &a;
In the above example p
is declared as a pointer to array of 10 int
s and it is actually initialized to point to such an array.
在上面的示例中,p被声明为指向10 ints数组的指针,它实际上被初始化为指向这样一个数组。
However, the term is also often used is its less formal meaning. In this example
然而,这个词也经常被用来表示不太正式的意思。在这个例子中
int a[10];
int *p = &a;
p
is declared as a mere pointer to int
. It is initialized to point to the first element of array a
. You can often hear and see people say that p
in this case also "points to an array" of int
s, even though this situation is semantically different from previous one. "Points to an array" in this case means "provides access to elements of an array through pointer arithmetic", as in p[5]
or *(p + 3)
.
p是声明仅仅是一个int。它是初始化指针指向第一个元素的数组,你可以经常听到和看到人们说p在这种情况下也指向一个数组的整数,尽管这种情况是语义上不同于前一个。“指向数组”在本例中表示“通过指针算法提供对数组元素的访问”,如p[5]或*(p + 3)。
This is exactly what is meant by the phrase "...argv
is a pointer to an array of pointers..." you quoted. argv
's declaration in parameter list of main
is equivalent to char **argv
, meaning that argv
is actually a pointer to a char *
pointer. But since it physically points to the first element of some array of char *
pointers (maintained by the calling code), it is correct to say semi-informally that argv
points to an array of pointers.
这正是“…”这句话的意思。argv是指向指针数组的指针。argv在main参数列表中的声明等价于char **argv,这意味着argv实际上是指向char *指针的指针。但是,由于它物理地指向一些char *指针数组的第一个元素(由调用代码维护),所以半非正式地说argv指向一个指针数组是正确的。
That's exactly what is meant by the text you quoted.
这正是你所引用的原文的意思。
#2
3
Where C functions claim to accept arrays, strictly they accept pointers instead. The language does not distinguish between void fn(int *foo) {}
and void fn(int foo[])
. It doesn't even care if you have void fn(int foo[100])
and then pass that an array of int [10]
.
当C函数声明接受数组时,它们严格地接受指针。该语言不区分void fn(int *foo){}和void fn(int foo[])。它甚至不关心是否有void fn(int foo[100])然后传递一个int[10]数组。
int main(int argc, char *argv[])
is the same as
是一样的
int main(int argc, char **argv)
Consequently, argv
points to the first element of an array of char
pointers, but it is not itself an array type and it does not (formally) point to a whole array. But we know that array is there, and we can index into it to get the other elements.
因此,argv指向char指针数组的第一个元素,但它本身并不是数组类型,也不是(正式地)指向整个数组。但是我们知道数组在那里,我们可以对它进行索引以获得其他元素。
In more complex cases, like accepting multi-dimensional arrays, it is only the first []
which drops back to a pointer (and which can be left unsized). The others remain as part of the type that is being pointed to, and they have an influence on pointer arithmetic.
在更复杂的情况下,比如接受多维数组,它仅仅是第一个返回到指针(并且可以被忽略)的[]。其他类型仍然是被指向的类型的一部分,它们对指针算法有影响。
#3
2
The array-pointer equivalence thing only holds true only for function arguments, so while void fn(const char* argv[])
and void fn(const char** argv)
are equivalent, it doesn't hold true when it comes to the variables you might want to pass TO the function.
array-指针等价性只适用于函数参数,所以当void fn(const char* argv[])和void fn(const char* argv)是等价的时,当涉及到要传递给函数的变量时,它并不成立。
Consider
考虑
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" is not a pointer to a pointer, it points directly to the first character in a 3*10 contiguous character sequence. Replace fn(vegetables) in the above to get
“蔬菜”不是指向指针的指针,它直接指向3*10连续字符序列中的第一个字符。将上面的fn(蔬菜)替换为get
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;
}
and the output is "A": vegetables itself is pointing directly - without indirection - to the characters, and not intermediate pointers.
输出是“A”:蔬菜本身直接指向字符,而不是中间指针。
The vegetables assignment is basically a shortcut for this:
蔬菜分配基本上是一个捷径:
const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0";
vegetables = __vegetablesPtr;
and
和
const char* roni = vegetables[2];
translates to
翻译为
const char* roni = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);
#4
0
Since argv is a pointer to an array of pointers
.
因为argv是指向指针数组的指针。
This is wrong. argv
is an array of pointers.
这是错误的。argv是一个指针数组。
#5
0
Since argv is a pointer to an array of pointers,
因为argv是指向指针数组的指针,
No, not even close.
不,甚至关闭。
Isn't
char *argv[]
just an array of pointers?char *argv[]不是一个指针数组吗?
No, it's a pointer to pointers.
不,它是指向指针的指针。
#6
0
"Pointer to the first element of an array" is a common construct. Every string function uses it, including stdio functions that input and output strings. main uses it for argv.
“指向数组的第一个元素的指针”是一个常见的构造。每个字符串函数都使用它,包括输入和输出字符串的stdio函数。主要用于argv。
"Pointer to an array" is a rare construct. I can't find any uses of it in the C standard library or POSIX. grepping all the headers I have installed locally (for '([^)]*\*[^)]) *\['
) I find exactly 2 legitimate instances of pointer-to-array, one in libjpeg and one in gtk. (Both are struct members, not function parameters, but that's beside the point.)
“指向数组的指针”是一个罕见的结构。我在C标准库或POSIX中找不到它的任何用途。grep的头安装在本地(“((^))* \ *(^)))* \[')我发现2合法pointer-to-array实例,一个在libjpeg,另一个在gtk。(两者都是结构成员,不是函数参数,但这不是重点。)
So if we stick to official language, we have a rare thing with a short name and a similar but much more common thing with a long name. That's the opposite of the way human language naturally wants to work, so there's tension, which gets resolved in all but the most formal situations by using the short name "incorrectly".
所以,如果我们坚持使用官方语言,我们就会有一个很罕见的名字,名字很短,但很常见,名字很长。这与人类语言自然想要的工作方式正好相反,所以存在着紧张感,这种紧张感在除了最正式的场合之外的所有场合都可以通过使用“不正确”的短名称得到解决。
The reason we don't just say "pointer to pointer" is that there's another common use of pointers as function parameters, in which the parameter points to a single object that's not a member of an array. For example, in
我们之所以不直接说“指针指向指针”,是因为指针作为函数参数的另一个常见用法是,参数指向一个不属于数组的对象。例如,在
long strtol(const char *nptr, char **endptr, int base);
endptr
is exactly the same type as argv
is in main
, both are pointer-to-pointer, but they're used in different ways. argv
points to the first char *
in an array of char *
s; inside main you're expected to use it with indexes like argv[0]
, argv[optind]
, etc., or step through the array by incrementing it with ++argv
.
endptr与argv的主类型完全相同,它们都是指针对指针,但是它们的使用方式不同。argv指向char *s数组中的第一个char *;在main中,您希望将它与argv[0]、argv[optind]等索引一起使用,或者使用++argv对数组进行递增。
endptr
points to a single char *
. Inside strtol
, it is not useful to increment endptr
or to refer to endptr[n]
for any value of n
other than zero.
endptr指向单个char *。在strtol内部,增加endptr或引用endptr[n]来表示除0之外的任何n值都是没有用的。
That's semantic difference is expressed by the informal usage of "argv is a pointer to an array". The possible confusion with what "pointer to array" means in formal language is ignored, because the natural instinct to use concise language is stronger than the desire to adhere to a formal definition that tells you not to use the most obvious simple phrase because it's reserved for a situation that will almost never happen.
这种语义差异通过“argv是指向数组的指针”的非正式用法来表示。可能的混淆与指针数组是指在正式语言将被忽略,因为天性使用简洁的语言比渴望坚持正式定义告诉你不使用最明显的简单短语因为它保留的情况几乎不会发生。
#1
7
The terms "pointer to array" or "to point to an array" are often treated rather loosely in C terminology. They can have at least two different things.
在C术语中,“指向数组的指针”或“指向数组”的术语通常处理得比较松散。他们至少可以有两种不同的东西。
In the most strict and pedantic sense of the term, a "pointer to array" has to be declared with "pointer to array" type, as in
在最严格和迂腐的意义上,“指向数组的指针”必须声明为“指向数组的指针”类型,如In
int a[10];
int (*p)[10] = &a;
In the above example p
is declared as a pointer to array of 10 int
s and it is actually initialized to point to such an array.
在上面的示例中,p被声明为指向10 ints数组的指针,它实际上被初始化为指向这样一个数组。
However, the term is also often used is its less formal meaning. In this example
然而,这个词也经常被用来表示不太正式的意思。在这个例子中
int a[10];
int *p = &a;
p
is declared as a mere pointer to int
. It is initialized to point to the first element of array a
. You can often hear and see people say that p
in this case also "points to an array" of int
s, even though this situation is semantically different from previous one. "Points to an array" in this case means "provides access to elements of an array through pointer arithmetic", as in p[5]
or *(p + 3)
.
p是声明仅仅是一个int。它是初始化指针指向第一个元素的数组,你可以经常听到和看到人们说p在这种情况下也指向一个数组的整数,尽管这种情况是语义上不同于前一个。“指向数组”在本例中表示“通过指针算法提供对数组元素的访问”,如p[5]或*(p + 3)。
This is exactly what is meant by the phrase "...argv
is a pointer to an array of pointers..." you quoted. argv
's declaration in parameter list of main
is equivalent to char **argv
, meaning that argv
is actually a pointer to a char *
pointer. But since it physically points to the first element of some array of char *
pointers (maintained by the calling code), it is correct to say semi-informally that argv
points to an array of pointers.
这正是“…”这句话的意思。argv是指向指针数组的指针。argv在main参数列表中的声明等价于char **argv,这意味着argv实际上是指向char *指针的指针。但是,由于它物理地指向一些char *指针数组的第一个元素(由调用代码维护),所以半非正式地说argv指向一个指针数组是正确的。
That's exactly what is meant by the text you quoted.
这正是你所引用的原文的意思。
#2
3
Where C functions claim to accept arrays, strictly they accept pointers instead. The language does not distinguish between void fn(int *foo) {}
and void fn(int foo[])
. It doesn't even care if you have void fn(int foo[100])
and then pass that an array of int [10]
.
当C函数声明接受数组时,它们严格地接受指针。该语言不区分void fn(int *foo){}和void fn(int foo[])。它甚至不关心是否有void fn(int foo[100])然后传递一个int[10]数组。
int main(int argc, char *argv[])
is the same as
是一样的
int main(int argc, char **argv)
Consequently, argv
points to the first element of an array of char
pointers, but it is not itself an array type and it does not (formally) point to a whole array. But we know that array is there, and we can index into it to get the other elements.
因此,argv指向char指针数组的第一个元素,但它本身并不是数组类型,也不是(正式地)指向整个数组。但是我们知道数组在那里,我们可以对它进行索引以获得其他元素。
In more complex cases, like accepting multi-dimensional arrays, it is only the first []
which drops back to a pointer (and which can be left unsized). The others remain as part of the type that is being pointed to, and they have an influence on pointer arithmetic.
在更复杂的情况下,比如接受多维数组,它仅仅是第一个返回到指针(并且可以被忽略)的[]。其他类型仍然是被指向的类型的一部分,它们对指针算法有影响。
#3
2
The array-pointer equivalence thing only holds true only for function arguments, so while void fn(const char* argv[])
and void fn(const char** argv)
are equivalent, it doesn't hold true when it comes to the variables you might want to pass TO the function.
array-指针等价性只适用于函数参数,所以当void fn(const char* argv[])和void fn(const char* argv)是等价的时,当涉及到要传递给函数的变量时,它并不成立。
Consider
考虑
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" is not a pointer to a pointer, it points directly to the first character in a 3*10 contiguous character sequence. Replace fn(vegetables) in the above to get
“蔬菜”不是指向指针的指针,它直接指向3*10连续字符序列中的第一个字符。将上面的fn(蔬菜)替换为get
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;
}
and the output is "A": vegetables itself is pointing directly - without indirection - to the characters, and not intermediate pointers.
输出是“A”:蔬菜本身直接指向字符,而不是中间指针。
The vegetables assignment is basically a shortcut for this:
蔬菜分配基本上是一个捷径:
const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0";
vegetables = __vegetablesPtr;
and
和
const char* roni = vegetables[2];
translates to
翻译为
const char* roni = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);
#4
0
Since argv is a pointer to an array of pointers
.
因为argv是指向指针数组的指针。
This is wrong. argv
is an array of pointers.
这是错误的。argv是一个指针数组。
#5
0
Since argv is a pointer to an array of pointers,
因为argv是指向指针数组的指针,
No, not even close.
不,甚至关闭。
Isn't
char *argv[]
just an array of pointers?char *argv[]不是一个指针数组吗?
No, it's a pointer to pointers.
不,它是指向指针的指针。
#6
0
"Pointer to the first element of an array" is a common construct. Every string function uses it, including stdio functions that input and output strings. main uses it for argv.
“指向数组的第一个元素的指针”是一个常见的构造。每个字符串函数都使用它,包括输入和输出字符串的stdio函数。主要用于argv。
"Pointer to an array" is a rare construct. I can't find any uses of it in the C standard library or POSIX. grepping all the headers I have installed locally (for '([^)]*\*[^)]) *\['
) I find exactly 2 legitimate instances of pointer-to-array, one in libjpeg and one in gtk. (Both are struct members, not function parameters, but that's beside the point.)
“指向数组的指针”是一个罕见的结构。我在C标准库或POSIX中找不到它的任何用途。grep的头安装在本地(“((^))* \ *(^)))* \[')我发现2合法pointer-to-array实例,一个在libjpeg,另一个在gtk。(两者都是结构成员,不是函数参数,但这不是重点。)
So if we stick to official language, we have a rare thing with a short name and a similar but much more common thing with a long name. That's the opposite of the way human language naturally wants to work, so there's tension, which gets resolved in all but the most formal situations by using the short name "incorrectly".
所以,如果我们坚持使用官方语言,我们就会有一个很罕见的名字,名字很短,但很常见,名字很长。这与人类语言自然想要的工作方式正好相反,所以存在着紧张感,这种紧张感在除了最正式的场合之外的所有场合都可以通过使用“不正确”的短名称得到解决。
The reason we don't just say "pointer to pointer" is that there's another common use of pointers as function parameters, in which the parameter points to a single object that's not a member of an array. For example, in
我们之所以不直接说“指针指向指针”,是因为指针作为函数参数的另一个常见用法是,参数指向一个不属于数组的对象。例如,在
long strtol(const char *nptr, char **endptr, int base);
endptr
is exactly the same type as argv
is in main
, both are pointer-to-pointer, but they're used in different ways. argv
points to the first char *
in an array of char *
s; inside main you're expected to use it with indexes like argv[0]
, argv[optind]
, etc., or step through the array by incrementing it with ++argv
.
endptr与argv的主类型完全相同,它们都是指针对指针,但是它们的使用方式不同。argv指向char *s数组中的第一个char *;在main中,您希望将它与argv[0]、argv[optind]等索引一起使用,或者使用++argv对数组进行递增。
endptr
points to a single char *
. Inside strtol
, it is not useful to increment endptr
or to refer to endptr[n]
for any value of n
other than zero.
endptr指向单个char *。在strtol内部,增加endptr或引用endptr[n]来表示除0之外的任何n值都是没有用的。
That's semantic difference is expressed by the informal usage of "argv is a pointer to an array". The possible confusion with what "pointer to array" means in formal language is ignored, because the natural instinct to use concise language is stronger than the desire to adhere to a formal definition that tells you not to use the most obvious simple phrase because it's reserved for a situation that will almost never happen.
这种语义差异通过“argv是指向数组的指针”的非正式用法来表示。可能的混淆与指针数组是指在正式语言将被忽略,因为天性使用简洁的语言比渴望坚持正式定义告诉你不使用最明显的简单短语因为它保留的情况几乎不会发生。