char* const argv[]是什么?

13

我正在学习Linux下的C++编程,突然看到了以下内容:

int execve(const char *path,
           char *const argv[],
           char *const envp[]);

我不理解 char *const argv[] 是什么意思。我知道 char *const foo 是一个指向字符的常量指针。而 const char *foo 则是一个指向常量字符的指针。但是 char *const argv[] 是什么呢?

它是一个指向字符的常量指针的数组,还是一个指向常量字符的指针的数组?

现在我有一个 vector<string>,如何将其转换为 char *const argv[]

3个回答

33

这是一个常量指针数组,还是一个指向常量字符的指针数组?

从右往左读类型:
const 总是与左边绑定(除非它在最左边)

char *const argv[]

                ^^ Array of
      ^^^^^        const
     ^             pointer to
^^^^               char

一个由指向常量指针的字符数组组成。

因此,数组中的指针是不能被修改的。
但它们所指向的内容可以被修改。

注意:如Steve所指出的。因为这被用作参数,它实际上会衰减为指向“指向常量指针的字符”或“char* const *” 的指针。

现在我有一个vector<string>,如何将其转换为char *const argv[]?

int execve(const char *path,
       char *const argv[],
       char *const envp[]);

int main()
{
    std::vector<std::string>   data;  /// fill
    std::vector<char*>         argv;
    std::vector<char*>         envp;

    for(std::vector<std::string>::iterator loop = data.begin(); loop != data.end(); ++loop)
    {
        argv.push_back(&(*loop)[0]);
    }

    // I also bet that argv and envp need to be NULL terminated
    argv.push_back(NULL);
    envp.push_back(NULL);
    execve("MyAppName", &argv[0], &envp[0]);
}

3
确实,char *const argv[] 是一个数组类型,但在这个上下文中,作为函数参数,它的类型不是 char *const argv[],而是 char *const *argv - Steve Jessop

4

cdecl.org 说:

char *const argv[]

declare argv as array of const pointer to char


如何将 vector<string> 转换为 char *const argv[] - Kaoet
@MrPhone:或许你可以将其作为一个单独的问题发布。这个问题很简单,但需要写出不止一条评论。 - Kerrek SB

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

在函数内部,你将无法改变"argv"。

由于你正在学习C语言,我建议你先真正尝试理解数组和指针之间的区别,而不是常见的东西。

在参数和数组的领域中,有一些令人困惑的规则,在继续之前应该清楚。首先,在参数列表中声明的内容是特殊处理的。在C语言中,有些情况下作为函数参数是没有意义的。这些情况包括:

  • 函数作为参数
  • 数组作为参数

数组作为参数

第二个可能不是立即清楚的。但是当你考虑到数组维度的大小是C语言类型的一部分时(而且维度大小未给出的数组具有不完整的类型),它就变得清晰了。因此,如果你创建一个按值接受数组(接收副本)的函数,则只能对一个大小进行操作!此外,数组可以变得很大,C语言试图尽可能快地运行。

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

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

扩展:函数作为参数

为了完整起见,并且因为我认为这将有助于更好地理解问题,让我们看看当你尝试将函数作为参数时情况如何。事实上,一开始它没有任何意义。参数怎么可能是一个函数?嗯,我们想要在那个位置放一个变量,当然!因此,当发生这种情况时,编译器将再次将函数转换为函数指针。尝试传递函数将传递指向该函数的指针。所以,以下内容是相同的(类似于数组示例):

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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接