我应该在C中使用char ** argv还是char * argv []?

时间:2009-04-23 01:02:56

标签: c arrays pointers standards

我只是在学习C,并且想知道我应该在我的主要方法中使用哪一个。有什么不同吗?

编辑:那么哪一个更常用?

10 个答案:

答案 0 :(得分:155)

由于您正在学习C,我建议您首先尝试理解数组和指针之间的差异而不是常见的事物。

在参数和数组领域,有一些令人困惑的规则应该在继续之前清楚。首先,您在参数列表中声明的内容将被视为特殊。在某些情况下,事物在C中作为函数参数没有意义。这些是

  • 作为参数的功能
  • 数组作为参数

数组作为参数

第二个可能不会立即清楚。但是当你认为数组维度的大小是C中类型的一部分时(而且没有给出维度大小的数组具有不完整的类型),就会变得很清楚。因此,如果您要创建一个采用数组按值(接收副本)的函数,那么它只能用于一个大小!此外,数组可能会变大,而C会尽可能快地尝试。

在C中,由于这些原因,数组值不存在。如果要获取数组的值,则获得的是指向该数组的第一个元素的指针。而这里实际上已经是解决方案了。 C编译器将转换相应参数的类型作为指针,而不是预先使数组参数无效。记住这一点,这非常重要。该参数不是数组,而是指向相应元素类型的指针。

现在,如果你尝试传递一个数组,那么传递的是指向数组第一个元素的指针。

Excursion:作为参数的函数

为了完成,并且因为我认为这将有助于您更好地理解这个问题,让我们看看当您尝试将函数作为参数时的状态。的确,首先它没有任何意义。参数如何成为函数?嗯,当然,我们想要一个变量!因此,当发生这种情况时编译器会再次将转换函数转换为函数指针。尝试传递函数将传递指向相应函数的指针。所以,以下是相同的(类似于数组示例):

void f(void g(void));
void f(void (*g)(void));

请注意,需要*g周围的括号。否则,它将指定一个返回void*的函数,而不是指向返回void的函数的指针。

返回数组

现在,我在开始时说过,数组可能有一个不完整的类型 - 如果你还没有给出一个大小,就会发生这种情况。由于我们已经认为数组参数不存在,而是任何数组参数都是指针,因此数组的大小无关紧要。这意味着,编译器将翻译以下所有内容,并且所有内容都是相同的:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

当然,能够在其中放置任何尺寸并没有多大意义,它只是扔掉了。出于这个原因,C99为这些数字提出了新的含义,并允许在括号之间出现其他内容:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

最后两行表示你将无法在函数中更改“argv” - 它已成为一个const指针。但是只有少数C编译器支持这些C99功能。但是这些特征清楚地表明“阵列”实际上并不是一个。这是一个指针。

一句警告

请注意,只有当您将数组作为函数的参数时,我所说的才是真的。如果使用本地数组,则数组将不是指针。它将表现为作为指针,因为如前所述,数组在读取其值时将转换为指针。但它不应该与指针混淆。

一个典型的例子如下:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

答案 1 :(得分:12)

您可以使用其中之一,这取决于您想要如何使用它。 char* argv[](大部分)等同于char ** argv。两种形式都指向指向char的指针,唯一的区别是使用char *argv[],您通知编译器argv的值不会改变(尽管它指向的值仍然可以)。实际上,我错了,他们完全等同。请参阅litb的评论和his answer

这取决于你想要如何使用它(你可以在任何情况下使用它):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

至于哪个更常见 - 没关系。任何有经验的C程序员阅读您的代码都会看到两者都是可以互换的(在合适的条件下)。就像一位经验丰富的英语演讲者一样容易读“他们是”和“他们”。

更重要的是,你要学会阅读它们并认识它们有多相似。你会阅读比你写的更多的代码,你需要同样适应两者。

答案 2 :(得分:9)

您可以使用两种形式中的任何一种,如在C数组中,指针在函数参数列表中可以互换。请参阅http://en.wikipedia.org/wiki/C_(programming_language)#Array-pointer_interchangeability

答案 3 :(得分:8)

它没有什么区别,但我使用char *argv[]因为它显示的是一个固定大小的可变长度字符串数组(通常是char *)。

答案 4 :(得分:4)

它并没有真正有所作为,但后者更具可读性。你得到的是一个char指针数组,就像第二个版本所说的那样。然而,它可以隐式转换为双字符指针,就像在第一个版本中一样。

答案 5 :(得分:1)

char **→指向字符指针的指针和char * argv []表示字符指针数组。因为我们可以使用指针而不是数组,所以都可以使用它们。

答案 6 :(得分:1)

你应该将它声明为char *argv[],因为它有许多相同的声明方式,最接近它的直观含义:字符串数组。

答案 7 :(得分:0)

我认为使用任何一种方法而不是另一种方法都没有特别的优点 - 使用与其余代码最相符的约定。

答案 8 :(得分:-2)

如果您需要变化或动态数量的字符串,char **可能更容易使用。如果你的字符串数是固定的,那么char * var []将是首选。

答案 9 :(得分:-2)

我知道这已经过时了,但是如果您只是学习C编程语言并且没有做任何重要的事情,请不要使用命令行选项。

如果您不使用命令行参数,请不要使用任何一个。只需将主函数声明为int main() 如果你

  • 希望您的程序用户能够将文件拖到您的程序中,以便您可以使用它来更改程序的结果或
  • 想要处理命令行选项(-help/?或终端或命令提示符中program name之后的任何其他内容)

使用对你更有意义的东西。 否则,只需使用int main() 毕竟,如果您最终想要添加命令行选项,可以在以后轻松编辑它们。