C++ - char** argv 与 char* argv[]

Posted

技术标签:

【中文标题】C++ - char** argv 与 char* argv[]【英文标题】:C++ - char** argv vs. char* argv[] 【发布时间】:2011-07-08 16:52:35 【问题描述】:

char** argvchar* 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** argvchar* argv[] 相同,因为在第二种情况下,“数组的名称是指向数组中第一个元素的 指针”。

从这里你也应该可以看到为什么数组索引从 0 开始。指向第一个元素的指针是数组的变量名(又是黄金法则)加上偏移量......什么都没有!

我曾与我的一位朋友争论过这里使用哪个更好。使用char* argv[] 表示法,读者可能更清楚这实际上是一个“字符指针数组”,而不是char** argv 表示法,后者可以被解读为“指向字符的指针”。我的观点是,后一种表示法并没有向读者传达太多信息。

很高兴知道它们完全相同,但为了便于阅读,我认为如果意图是一个指针数组,那么char* argv[] 表示法更清楚地传达了这一点。

【讨论】:

感谢您的回复。您能告诉我这两种形式有何相同之处吗? 我已经编辑了我的答案,应该给出更详细的解释。 “要记住的数组的黄金法则是” 绝对不是你写的。这是一个可怕的、令人沮丧的神话,它不会死。数组的名称可以衰减为指向该数组第一个元素的指针。这与等价或同一性绝对不同。 @JamesBedford:不,这不是真正的编译时与运行时。像int x[10] 这样的数组是一个由 10 个整数组成的堆栈变量。 (当它的封闭范围放在堆栈上时,它的框架包括这 10 个整数的空间。)它不是一个指针,它是一个包含某物地址的变量。当你创建一个数组时,堆栈上没有存储地址,而不是当你创建一个指针时。但是,当您将数组传递给函数时,它衰减;该函数接收一个指针,而不是一个数组。 考虑:当你取一个数组的地址时,&amp;x,你得到的是数组中数据的地址。如果x 是一个指针,那么您将获得保存指向数据的指针的内存位置的地址,但您没有。【参考方案3】:

在 C 和 C++ 中,TYPE * NAMETYPE 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 &lt;&lt; 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 的指针数组读取,并且数组参数被降级为指针,因此指向 charchar ** 的指针的指针。

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** argvpointer 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*[] 不同

C++ 是不是将 char 指针视为 c 样式字符串?

char **argv 与char *argv[]

c++中wchar转char

指向 char 指针数组的指针与指向 char 指针的指针(或 char** argv 与 char* (*argv)[])