我正在读“操作系统:三个简单的部分”。在第五章中,有一段代码展示了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 failed\n");
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 out\n");
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;它的工作原理和前一个一样。
发布于 2021-10-09 13:09:59
代码会根据您的更改进行编译,但可能会触发未定义的行为。
execvp需要参数char *const argv[]。而不是char上丢失的const。因此,execvp可以自由地写入传递给它的字符串。
String literals in C不可修改:
字符串文字是不可修改的(实际上可以放在只读内存中,比如.rodata)。如果程序试图修改由字符串文字形成的静态数组,则行为是未定义的。
所以代码的作者希望是安全的,并对字符串文字进行strdup,以获得字符串的可修改版本。
请注意,C++是更严格的,在那里,gcc会发出
警告: ISO C++禁止将字符串常量转换为'char*‘-Wwrite- string
用于您的代码。
如果execvp不修改arg字符串,那么最好有一个表示这一点的签名(const char *const argv[])。
发布于 2021-10-09 17:33:16
您的代码版本很好。与Werner的回答相反,它不会导致未定义的行为。
根据the POSIX spec的说法,execvp的功能原型是
int execvp(const char *file, char *const argv[]);单独考虑,这意味着可以修改argv数组中字符串的内容,尽管该数组的指针元素本身不能修改。如果是这样,那么确实有必要传递一个指向可写字符串的指针数组,比如strdup提供的,而不是字符串文字,因为写入字符串文字确实是未定义的行为。这可能就是代码的作者所想的。
然而,规范的文本为我们提供了更多信息:
指针的argv[]和envp[]数组以及这些数组所指向的字符串不得通过调用其中一个exec函数来修改,除非是替换进程映像的结果。
因此,事实上,可以保证execvp不会修改这些字符串。因此,传递字符串字面量是完全安全的。原始代码的作者可能不知道这个保证。
下面的理由解释了为什么他们选择了原型(“关于argv[]和envp[]是常量的声明包含在...”)。正如沃纳建议的那样,const char *const argv[]似乎更好地表达了实际行为。然而,C的指针转换规则意味着,如果它们有,那么代码就像
char *args[5];
args[0] = /* some writable string */;
...
execvp(file, args);不会编译,即使在原则上它是完全没问题的。其基本原理包括对const在C中的语义以及它们如何使其无法正确编写的礼貌含糊的大肆抨击。
在Can I pass a const char* array to execv?上还有一些进一步的讨论。
https://stackoverflow.com/questions/69505245
复制相似问题