C语言 为什么这里使用strdup() ?



我在读"操作系统:三块简单"。在第5章中,有一段代码显示了exec()系统调用的用法。

1 #include "common.h"
2
3 int main(int argc, char ** argv) {
4     printf("hello world (pid: %d)n", (int) getpid());
5     int rc = fork();
6     if (rc < 0) {
7         fprintf(stderr, "fork failedn");
8         exit(1);
9     } else if (rc == 0) {
10         printf("hello, I am child (pid: %d)n", (int) getpid());
11         char *myargs[3];
12         myargs[0] = strdup("wc");
13         myargs[1] = strdup("p3.c");
14         myargs[2] = NULL;
15         execvp(myargs[0], myargs); // run work count
16         printf("this shouldn't print outn");
17     } else {
18         int wc = wait(NULL);
19         printf("hello, I am parent of %d (wc: %d) (pid: %d)n", rc, wc, (int) getpid());
20     }
21     return 0;
22 }
23

为什么在第12~14行使用strdup() ?我已经尝试用以下内容替换此部分:

12         myargs[0] = "wc";                                                                                                                                                                                     
13         myargs[1] = "p3.c";
14         myargs[2] = NULL;

和前一个一样。

您的代码版本很好。与Werner的回答相反,它不会导致未定义行为。

根据POSIX规范,execvp的函数原型为

int execvp(const char *file, char *const argv[]);

单独来看,这表明argv数组中字符串的内容可以被修改,尽管该数组的指针元素本身不能被修改。如果是这样的话,那么确实有必要传递一个指针数组给可写的字符串,比如strdup将提供,而不是字符串字面量,因为写入字符串字面量确实是未定义的行为。这可能是代码的作者所想的。

然而,规范的文本给了我们更多的信息:

指针的argv[]和envp[]数组以及这些数组所指向的字符串不能通过调用exec函数来修改,除非是替换进程映像的结果。

所以事实上execvp保证不修改这些字符串。因此,传递字符串字面值是完全安全的。原始代码的作者可能不知道这个保证。

基本原理进一步解释了为什么他们选择了原型("关于argv[]和envp[]是常量的声明包含在…")。正如Werner所说,const char *const argv[]似乎更好地表达了实际行为。然而,C的指针转换规则意味着,如果它们有,那么像

这样的代码
char *args[5];
args[0] = /* some writable string */;
...
execvp(file, args);

将无法编译,尽管原则上它是完全正确的。其基本原理包括礼貌地对C语言中const的语义以及它们如何使其无法正确编写的隐晦咆哮。

可以将const char*数组传递给execv吗?

代码会随着您的更改而编译,但它可能会触发未定义的行为。

execvpchar *const argv[]预计参数。不是char上缺失的const。因此,execvp可以自由地写入传递给它的字符串。

C中的字符串字面值是不可修改的:

字符串字面值是不可修改的(实际上可以放在只读内存中,如.rodata)。如果程序试图修改由字符串字面值组成的静态数组,则该行为是未定义的。

因此,代码的作者希望安全起见,因此strdup使用字符串字面值来获得字符串的可修改版本。

请注意c++更严格,gcc会发出

警告:ISO c++禁止将字符串常量转换为'char*' [-Wwrite-strings]

:

如果execvp不修改参数串,那么如果它有一个表示这个(const char *const argv[])的签名会更好。

最新更新