C++ - char** argv 与 char* argv[]
Posted
技术标签:
【中文标题】C++ - char** argv 与 char* argv[]【英文标题】:C++ - char** argv vs. char* argv[] 【发布时间】:2011-07-08 16:52:35 【问题描述】:char** argv
和 char* argv[]
有什么区别?在int main(int argc, char** argv)
和int main(int argc, char* argv[])
?
它们是一样的吗?特别是第一部分没有[]
。
【问题讨论】:
【参考方案1】:这是我想出的一个简单示例,它有两个函数(Main_1、Main_2)与主函数采用相同的参数。
我希望这能把事情弄清楚..
#include <iostream>
void Main_1(int argc, char **argv)
for (int i = 0; i < argc; i++)
std::cout << *(argv + i) << std::endl;
void Main_2(int argc, char *argv[])
for (int i = 0; i < argc; i++)
std::cout << *(argv + i) << std::endl;
int main()
// character arrays with null terminators (0 or '\o')
char arg1[] = 'h', 'e', 'l', 'l', 'o', 0;
char arg2[] = 'h', 'o', 'w', 0;
char arg3[] = 'a', 'r', 'e', '\0';
char arg4[] = 'y', 'o', 'u', '\n', '\0';
// arguments count
int argc = 4;
// array of char pointers (point to each character array (arg1, arg2, arg3 and arg4)
char *argPtrs[] = arg1, arg2, arg3, arg4;
// pointer to char pointer array (argPtrs)
char **argv = argPtrs;
Main_1(argc, argv);
Main_2(argc, argv);
// or
Main_1(argc, argPtrs);
Main_2(argc, argPtrs);
return 0;
输出:
hello
how
are
you
hello
how
are
you
hello
how
are
you
hello
how
are
you
【讨论】:
【参考方案2】:它们确实是一模一样的。
要记住的数组黄金法则是:
“数组名是指向数组第一个元素的指针。”
因此,如果您声明以下内容:
char text[] = "A string of characters.";
那么变量“text”是一个指向你刚刚声明的字符数组中第一个字符的指针。换句话说,“文本”的类型为char *
。当您使用 [index] 访问数组的元素时,您实际上正在做的是将 index 的偏移量添加到指向数组第一个元素的指针,然后取消引用这个新指针。因此,以下两行会将两个变量都初始化为 't':
char thirdChar = text[3];
char thirdChar2 = *(text+3);
使用方括号是语言提供的一种便利,可以使代码更具可读性。但是,当您开始考虑更复杂的事情(例如指向指针的指针)时,这种工作方式非常重要。 char** argv
与 char* argv[]
相同,因为在第二种情况下,“数组的名称是指向数组中第一个元素的 指针”。
从这里你也应该可以看到为什么数组索引从 0 开始。指向第一个元素的指针是数组的变量名(又是黄金法则)加上偏移量......什么都没有!
我曾与我的一位朋友争论过这里使用哪个更好。使用char* argv[]
表示法,读者可能更清楚这实际上是一个“字符指针数组”,而不是char** argv
表示法,后者可以被解读为“指向字符的指针”。我的观点是,后一种表示法并没有向读者传达太多信息。
很高兴知道它们完全相同,但为了便于阅读,我认为如果意图是一个指针数组,那么char* argv[]
表示法更清楚地传达了这一点。
【讨论】:
感谢您的回复。您能告诉我这两种形式有何相同之处吗? 我已经编辑了我的答案,应该给出更详细的解释。 “要记住的数组的黄金法则是” 绝对不是你写的。这是一个可怕的、令人沮丧的神话,它不会死。数组的名称可以衰减为指向该数组第一个元素的指针。这与等价或同一性绝对不同。 @JamesBedford:不,这不是真正的编译时与运行时。像int x[10]
这样的数组是一个由 10 个整数组成的堆栈变量。 (当它的封闭范围放在堆栈上时,它的框架包括这 10 个整数的空间。)它不是一个指针,它是一个包含某物地址的变量。当你创建一个数组时,堆栈上没有存储地址,而不是当你创建一个指针时。但是,当您将数组传递给函数时,它衰减;该函数接收一个指针,而不是一个数组。
考虑:当你取一个数组的地址时,&x
,你得到的是数组中数据的地址。如果x
是一个指针,那么您将获得保存指向数据的指针的内存位置的地址,但您没有。【参考方案3】:
在 C 和 C++ 中,TYPE * NAME
和 TYPE NAME[]
之间存在差异。在 C++ 中,这两种类型是不可互换的。例如下面的函数在 C++ 中是非法的(你会得到一个错误),但在 C 中是合法的(你会得到警告):
int some (int *a[3]) // a is array of dimension 3 of pointers to int
return sizeof a;
int main ()
int x[3][3];
std::cout << some(x)<< std::endl;
return 0;
要使其合法,只需将签名更改为 int some (int (*a)[3])
(指向 3 个整数数组的指针)或 int some (int a[][3])
。最后方括号中的数字必须等于参数的数字。从数组数组转换为指针数组是非法的。从指针转换为指向数组数组的指针也是非法的。但是将指针转换为指向指针数组的指针是合法的!
所以请记住:只有最接近解引用类型签名才无关紧要,其他人则如此(当然,在指针和数组的上下文中)。
假设我们有一个指向 int 的 as 指针:
int ** a;
&a -> a -> *a -> **a
(1) (2) (3) (4)
-
您无法更改此值,类型为
int ***
。可以被函数取为int **b[]
或int ***b
。最好的是int *** const b
。
类型为int **
。可以被函数取为int *b[]
或int ** b
。数组声明的括号可以留空或包含任何数字。
类型为int *
。可以通过函数作为int b[]
或int * b
甚至void * b
应作为 int 参数。我不想陷入细节,比如隐式构造函数调用。
回答你的问题:真正的参数类型在主函数是char ** argv
,所以它可以很容易地表示为char *argv[]
(但不是@987654342 @)。另外argv
main 函数的名称可能会更改为safely。
您可以轻松查看:std::cout << typeid(argv).name();
(PPc = 指向 p. 到 char 的指针)
顺便说一句:有一个很酷的功能,将数组作为引用传递:
void somef(int (&arr)[3])
printf("%i", (sizeof arr)/(sizeof(int))); // will print 3!
此外指向任何东西的指针可能被函数隐式接受(转换)为 void 指针。但只有单个指针(不是指向指针等的指针)。
进一步阅读:
-
Bjarne Stroustrup,C++,第 7.4 章
C pointers FAQ
【讨论】:
“C 和 C++ 中的 TYPE * NAME 和 TYPE NAME[] 是有区别的。” 废话。 “在 C++ 中,这两种类型是不可互换的。” 不,只有第一种类型实际上存在。 “例如下面的函数是非法的(你会得到一个错误)” 那是因为int* ar[3]
和int* ar[]
是完全不同的东西。错误信息不断……
clothest
是什么意思??
@Dan 我的意思是最近的,抱歉
@yanpas 如果您愿意,可以编辑您的答案进行更正。【参考方案4】:
对于问题的第一部分:
char** argv:指向 char 指针的指针 char* argv[]: 指向数组的指针所以问题是指向类型 C 的指针和数组 C[] 是否相同。它们根本不是一般的,但它们是equivalent when used in signatures。
换句话说,您的示例没有区别,但重要的是要记住指针和数组之间的区别。
【讨论】:
链接是 C 常见问题解答,但问题标记为 C++。 C++ 是 C 的扩展...所以适用于 C 的一切都与 C++ 相关。 不。在 C 中有一些想法是非法的,在 C++ 中是允许的,所以当一些消息来源说“它是非法的……”时,并不意味着这适用于 C++。 C 和 C++ 在这方面非常相似。 "char* argv[]: 指向数组的指针" 否。T* param[]
在语法级别被重写为T** param
。您可以将 T**
传递给与数组无关的函数。【参考方案5】:
除了以下细微差别外,两者的用法相同:
Sizeof 会给出不同的结果 第二个也不能重新分配到新的内存区域,因为它是一个数组 对于第二个,您只能使用那些索引 有效的。如果您尝试使用超出的数组索引,则 C/C++ 未指定 数组长度。但是使用 char** 你可以使用从 0 到 ... 的任何索引 第二种形式只能用作函数的形式参数。虽然 first 甚至可以用于在堆栈中声明变量。【讨论】:
什么?在 OP 要求的上下文中,每一个都是不正确的。【参考方案6】:括号形式仅在以下语句声明中有用:
char *a[] = "foo", "bar", "baz";
printf("%d\n", sizeof a / sizeof *a);
// prints 3
因为它在编译时就知道数组的大小。当您将括号形式作为参数传递给函数(main 或其他函数)时,编译器不知道数组在运行时的大小,因此它与 char **a 完全相同。我更喜欢 char **argv,因为更清楚的是 sizeof 不会像在声明声明表单上那样工作。
【讨论】:
【参考方案7】:它们是完全等价的。 char *argv[]
必须作为指向 char
的指针数组读取,并且数组参数被降级为指针,因此指向 char
或 char **
的指针的指针。
C 也是这样。
【讨论】:
感谢您的回复。你能再解释一下吗:...and an array argument is demoted to a pointer, so pointer to pointer to char, or char **.
@user:这是语言规则。当您使用语法X foo(Y a[])
声明或定义函数时,它实际上变为X foo(Y *a)
。看起来像函数的数组参数实际上是一个指针。由于argv
被声明为一个数组(指针),它变成了一个指针(指向指针)。
char** argv
的 pointer to char 部分是明确的。但是,第一个指针是从哪里来的呢?
@user:让我们做一个typedef char *String
,所以String
是指向char
的指针的别名。现在argv
可以声明为String argv[]
,这是一个String
的数组。既然是参数,那它确实是指向String
的指针,所以String *argv
。我希望这能让你更清楚,我并没有真正理解你的问题。
所以...如果我理解正确,我们可以说... 1. 我们有一个指向数组的指针(*argv 与 argv[] 相同) 2. 在该数组中,我们有几个指向字符的指针【参考方案8】:
出于所有实际目的,它们是相同的。这是由于 C/C++ 对作为参数传递的数组的处理,其中数组衰减为指针。
【讨论】:
以上是关于C++ - char** argv 与 char* argv[]的主要内容,如果未能解决你的问题,请参考以下文章
C++ int main (int argc, char *argv[]) - argv 是 c 风格的数组吗?
c++:函数 arg char** 与 char*[] 不同
指向 char 指针数组的指针与指向 char 指针的指针(或 char** argv 与 char* (*argv)[])