C ++ – char ** argv与char * argv

char** argvchar* argv[]什么区别? 在int main(int argc, char** argv)int main(int argc, char* argv[])

他们是一样的吗? 特别是第一部分没有[]

他们完全相同。 char *argv[]必须被读为指向char的指针数组,并且数组参数被降级为指针,所以指向指向charchar **指针。

这在C中是一样的。

他们确实是一模一样的。

数组要记住的黄金法则是:

“数组的名称是指向数组的第一个元素的指针”。

所以如果你声明如下:

char text[] = "A string of characters.";

然后,variables“text”是一个指向刚刚声明的字符数组中第一个字符的指针。 换句话说,“文本”是char *types的。 当你使用[ index ]访问一个数组的元素时,你实际上做的是将索引的偏移量添加到指向数组第一个元素的指针,然后解引用这个新的指针。 以下两行将因此将两个variables初始化为't':

 char thirdChar = string[3]; char thirdChar2 = *(string+3); 

使用方括号是语言提供的便利,使代码更加可读。 但是,当你开始考虑更复杂的事情时,这种方式非常重要,例如指向指针的指针。 char** argvchar* argv[]相同,因为在第二种情况下,“数组的名称是指向数组中第一个元素的指针 ”。

从这里你也应该能够明白为什么数组索引是从0开始的。指向第一个元素的指针是数组的variables名(再次是黄金法则)加上偏移量…什么都不是!

我和我的一个朋友进行过辩论,哪个更适合在这里使用。 使用char* argv[]符号,读者可能会更清楚地知道,这实际上是一个“指向字符的指针数组”,而不是char** argv表示法,它可以被读为“指向指向字符”。 我的看法是,后面的这个表示法并没有向读者传达太多的信息。

很高兴知道它们完全一样,但为了可读性,我认为如果意图是一个指针数组,那么char* argv[]表示法就更清楚地expression了这一点。

为了所有的实际目的,他们是一样的。 这是由于C / C ++对作为parameter passing的数组的处理,在这种情况下,一个数组衰减到一个指针。

对于问题的第一部分:

  • char ** argv:指向char的指针
  • char * argv []:指向数组的指针

所以问题是一个typesC和一个数组C []的指针是否是相同的东西。 它们一般都不是,但是在签名中使用它们是等价的 。

换句话说,在你的例子中没有什么区别,但是要牢记指针和数组之间的区别是很重要的。

括号forms仅在语句声明中有用,如:

 char *a[] = {"foo", "bar", "baz"}; printf("%d\n", sizeof a / sizeof *a); // prints 3 

因为它在编译时知道数组的大小。 当你将一个括号forms作为parameter passing给一个函数(main或者其他函数)时,编译器不知道在运行时数组的大小是多less,所以它和char ** a完全一样。 我更喜欢char ** argv,因为它更清晰,sizeof不会像在声明声明表单上那样工作。

在C和C ++中, TYPE * NAMETYPE NAME[]都有区别。 在C ++中,这两种types都不可互换。 例如下面的函数在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的数组的指针)或int some (int a[][3]) 。 最后方括号中的数字必须等于参数的数字。 从数组数组转换为指针数组是非法的。 从指针转换为指向数组数组也是非法的。 但是将指针转换为指针数组是合法的!

所以记住: 只有最接近于解引用types签名并不重要,其他人也可以 (在指针和数组的上下文中)。

考虑我们有一个指向int指针的指针:

 int ** a; &a -> a -> *a -> **a (1) (2) (3) (4) 
  1. 你不能改变这个值,types是int *** 。 可以通过函数作为int **b[]int ***b 。 最好的是int *** const b
  2. types是int ** 。 可能被函数作为int *b[]int ** b 。 数组声明的括号可以是空的或包含任何数字。
  3. 该types是int * 。 可能被函数作为int b[]int * b甚至void * b
  4. 应该作为int参数。 我不想陷入细节,就像隐式的构造函数调用一样。

回答你的问题: 主函数中argumets的真实types是char ** argv ,所以它可以很容易地表示为char *argv[] (但不是char (*argv)[] )。 主函数的argv名也可以安全地改变。 你可以很容易地检查: std::cout << typeid(argv).name(); (PPc =指向p的指针)

顺便说一句:有一个很酷的function,传递数组作为参考:

 void somef(int (&arr)[3]) { printf("%i", (sizeof arr)/(sizeof(int))); // will print 3! } 

而且,指向任何东西的指针都可以被函数隐式地接受(转换)为空指针。 但是只有单个指针(不是指针指针等)。

进一步阅读:

  1. Bjarne Stroustrup,C ++,第7.4章
  2. C指针FAQ

除了以下细微的区别之外,两者都是一样的:

  • Sizeof会给两个不同的结果
  • 第二个可能不会被重新分配到新的内存区域,因为它是一个数组
  • 第二个你可以只使用那些有效的索引。 如果您尝试使用超出数组长度的数组索引,则C / C ++未指定它。 然而,用char **,你可以使用从0到…的任何索引
  • 第二种forms只能用作函数的forms参数。 虽然第一个甚至可以用来声明栈中的variables。