可以在 posix_spawn 的 argv 中传递字符串文字吗?

Can string literals be passed in posix_spawn's argv?

对于the posix_spawn function其原型是:

int posix_spawn(pid_t *restrict pid, const char *restrict path,
   const posix_spawn_file_actions_t *file_actions,
   const posix_spawnattr_t *restrict attrp,
   char *const argv[restrict], char *const envp[restrict]);

值得注意的是,argv 参数指向一个 char * 指针数组(即指向可变字符的指针)。此外,该文档似乎并不能保证数据不会被写入。

我的问题是:在某处是否可以保证可以传递字符串文字?还是我们冒着段错误的风险?

示例代码:

char *v[] = { "foo.exe", "bar", NULL };
posix_spawn( NULL, "foo.exe", NULL, NULL, v, NULL );

我很确定选择该类型是为了与 main(和 execve)的 char **argv 参数兼容。 (尽管在具有适当进程分离的传统实现中,内核最终必须进行复制。)

POSIX 似乎并没有说这些数组被修改了,但我非常有信心现有的实现不会修改它们。使用不同的参数(和可执行文件名称)可能有一些原因,但这些参数会 更长 ,因此 posix_spawn 无论如何都必须为副本分配内存,并且无法执行就地修改。

is there a guarantee somewhere that it is OK to pass a string literal? Or are we risking a segfault?

考虑到,存在技术段错误风险,因为 argv 需要 char* 的非 const 数组并提供 字符串文字 可导致 UB。使用一个函数可能建模 main(int argc, char *argv[]),代码可以写入 argv[0].

int main(int argc, char *argv[])
...
The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination. C11 §5.1.2.2.1 2

int foo(......., char *const argv[restrict], char *const envp[restrict]);

char *v[] = { "foo.exe", "bar", NULL };
foo( NULL, "foo.exe", NULL, NULL, v, NULL );

备选

虽然使用 posix_spawn(),但我怀疑是否会发生写入,C99 的解决方案使用 复合文字 而不是 字符串文字 这样就避免了 UB 的可能性。

// char *v[] = { "foo.exe", "bar", NULL };
char *v2[] = { (char [8]){"foo.exe"}, (char [4]){"bar"}, NULL };
posix_spawn( NULL, "foo.exe", NULL, NULL, v2, NULL );

现在 posix_spawn() 可以写入 v2[0]

在这里使用字符串文字非常好。

指针参数(或参数指向的指针数据)是否指向const限定类型与函数是否可以修改指向的对象无关。这纯粹是相关功能的契约问题。作为惯例,当对象不被修改时,通常最好在参数中使用 const 限定指针:

  1. 允许在不强制转换的情况下将指针传递给 const 限定的对象,并且
  2. 表示对象不会被修改。

但是在C语言中没有要求这样做。对于在其接口中使用双指针类型的函数,这里通常需要权衡。由于 T *const T * 不能互为别名,接口必须选择调用者更可能需要的形式;如果调用者想要其他形式,它必须制作一个临时副本以传递给函数。 posix_spawn.

就是这种情况

一般来说,当涉及到标准函数(C 或 POSIX)时,它们 不会有任何可观察到的副作用,除非指定 。除非函数的描述说明它将修改一个对象 "belonging to" 应用程序,或者应用程序可以访问的对象,否则它不能修改它;这样做是不合格的。这就是 return 指向静态存储的函数明确记录它的原因。例如,POSIX 文档 strerror:

The returned string pointer might be invalidated or the string content might be overwritten by a subsequent call to strerror(),

缺少此类文档,应用程序可能会假设由 strerror 编辑的字符串 return 从未被实现修改。

由于 posix_spawn 没有记录修改其 argv 数组指向的字符串,因此不会修改它们。

此外,请注意 posix_spawn 需要线程安全,并且不会对应用程序并发访问 argv 字符串施加任何明确的限制。因此,任何修改都会引入数据竞争,从而导致 posix_spawn 非线程安全,这与规范相反。