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

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

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

【问题讨论】:

    标签: c++ main arguments


    【解决方案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)
      
      1. 您无法更改此值,类型为int ***。可以被函数取为int **b[]int ***b。最好的是int *** const b
      2. 类型为int **。可以被函数取为int *b[]int ** b。数组声明的括号可以留空或包含任何数字。
      3. 类型为int *。可以通过函数作为int b[]int * b 甚至void * b
      4. 应作为 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 指针。但只有单个指针(不是指向指针等的指针)。

      进一步阅读:

      1. Bjarne Stroustrup,C++,第 7.4 章
      2. 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++ 对作为参数传递的数组的处理,其中数组衰减为指针。

        【讨论】:

          猜你喜欢
          • 2022-07-14
          • 2015-12-21
          • 2010-10-21
          • 2015-10-24
          • 2021-11-09
          • 1970-01-01
          • 1970-01-01
          • 2011-01-04
          • 2018-04-05
          相关资源
          最近更新 更多