对于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 );
答案 0 :(得分:2)
在这里使用字符串文字非常好。
指针参数(或参数指向的指针数据)是否指向const限定类型与函数是否可以修改指向对象无关。这纯粹是有关功能合同的问题。作为惯例,当对象不被修改时,通常最好在参数中使用const限定指针:
但是没有要求在C语言中这样做。对于在其接口中使用双指针类型的函数,这里通常需要权衡。由于T *
和const T *
不能互为别名,因此界面必须选择更有可能让调用者想要的格式;如果调用者想要另一个表单,则必须将临时副本传递给该函数。这是posix_spawn
的情况。
一般来说,当涉及标准功能(C或POSIX)时,除了指定的之外,不能有任何可观察到的副作用。除非函数文档描述它将修改对象“属于”应用程序,或者应用程序可以访问的对象,否则它不能修改它;这样做是不合格的。这就是返回指向静态存储指针的函数显式记录它的原因。例如,strerror
的POSIX文档:
返回的字符串指针可能无效,或者后续调用strerror()可能会覆盖字符串内容,
如果缺少此类文档,应用程序可能会认为strerror
返回的字符串永远不会被实现修改。
由于未记录posix_spawn
来修改其argv
数组指向的字符串,因此不会修改它们。
此外,请注意posix_spawn
必须是线程安全的,并且不会对应用程序对argv
字符串的并发访问进行任何显式约束。因此,任何修改都会引入数据竞争,从而使posix_spawn
非线程安全,与规范相反。
答案 1 :(得分:1)
我非常确定选择的类型与java-8-openjdk
{和char **argv
的{{1}}参数兼容。 (虽然在传统的实现具有适当的进程分离的情况下,内核最终必须进行复制。)
POSIX似乎没有说这些数组已被修改,但我非常有信心没有现有的实现会修改它们。使用不同的参数(和可执行文件名)可能有一些原因,但那些更长,因此main
必须为副本分配内存,并且不能在以下情况下执行修改:的地方。
答案 2 :(得分:1)
是否可以保证传递字符串文字?或者我们冒着一个段错误的风险?
鉴于,argv
期望非const
数组char*
并提供字符串文字可导致UB,因此存在技术段错误风险。使用可能建模main(int argc, char *argv[])
的函数,代码可以写入argv[0]
。
int main(int argc, char *argv[])
...
参数argc
和argv
以及argv
数组指向的字符串应由程序可修改,并在程序启动之间保留其最后存储的值和程序终止。 C11§5.1.2.2.12
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]