execv() 和 const-ness
Posted
技术标签:
【中文标题】execv() 和 const-ness【英文标题】:execv() and const-ness 【发布时间】:2008-10-10 05:06:55 【问题描述】:我经常在 C++ 中使用execv()
函数,但如果某些参数是 C++ 字符串,我会因为不能这样做而烦恼:
const char *args[4];
args[0] = "/usr/bin/whatever";
args[1] = filename.c_str();
args[2] = someparameter.c_str();
args[3] = 0;
execv(args[0], args);
这不会编译,因为execv()
采用与const char *
不兼容的char *const argv[]
,所以我必须使用strdup()
将我的std::string
s 复制到字符数组中,这很痛苦。
有人知道这是什么原因吗?
【问题讨论】:
如果你确定你执行的程序不会改变它的参数,我认为你可以安全地使用 const_cast。 execv() 的实现没有。 我一直在为我所做的每个系统调用编写 C++ 包装器,因此系统调用会引发异常,而我不必检查返回值。作为其中的一部分,我刚刚创建了一组重载,它们为我完成了所有const_cast
ing,因此我不必将它们洒在整个代码中。您需要 execve 的定义才能工作,因为这两个参数都是 const/non-const。其中之一当然是系统定义。其他的只是内联函数,在给定适当的演员表的情况下转发给那个。
【参考方案1】:
开放组基本规范解释了这是为什么:为了与现有 C 代码兼容。但是,指针和字符串内容本身都不打算更改。因此,在这种情况下,您可以使用const_cast
-ing c_str()
的结果来逃避。
Quote:
包含关于
argv[]
和envp[]
是常量的声明是为了向未来的语言绑定编写者明确说明这些对象是完全常量。由于 ISO C 标准的限制,不可能在标准 C 中陈述这个想法。为 exec 函数的argv[]
和envp[]
参数指定两个级别的const
- 限定似乎是自然选择,因为这些函数不修改指针数组或函数指向的字符,但这将不允许现有的正确代码。相反,只有指针数组被标记为常量。
之后的表格和文字更有洞察力。但是,Stack Overflow 不允许插入表格,因此上面的引用应该足以让您在链接文档中搜索正确的位置。
【讨论】:
即使乔纳森的回答相似,我也会接受这个答案。在我看来,这个措辞更好。 谢谢!我只是在引用的文章中添加了一点说明人们应该继续阅读的内容;它更好地解释了基本原理,以及如果不是为了兼容性,char const* const[] 会更可取。 @user877329 声明位于<unistd.h>
中,只有您的操作系统提供商可以更改。当然,您不能期望用户代码能够覆盖其内容。
@ChrisJester-Young。我和标准委员会谈过
@user877329 - #ifdef __cplusplus__
中应该包含一堆内联重载,在适当的强制转换后转发到主系统函数。该表提供了一个令人信服的案例,这是确保函数采用它应该采用的所有不同参数类型的唯一方法。【参考方案2】:
const 是 C++ 的东西 - execv 在 C++ 存在之前就采用了 char * 参数。
您可以使用 const_cast 而不是复制,因为 execv 实际上并没有修改它的参数。您可能会考虑编写一个包装器来节省自己的打字时间。
实际上,您的代码的一个更大问题是您声明了一个字符数组而不是字符串数组。
尝试: const char* args[4];
【讨论】:
哦,我的意思是输入 const char *,要么是我输入错误,要么是堆栈溢出占用了我的星号。我将编辑问题。 execv 不会接受 const char *arg[] - 至少不接受 g++,但感谢您的回答,我确实考虑过强制转换为非常量,但我不知道这是否安全。 C 编译器允许您将 const char* 传递给 execv,但它会发出警告。但是,对于 C++ 编译器,您需要使用 const_cast。 "const 是 C++ 的东西" -- const 是 C 的东西,在 C++ 中标准化之前在 C 中标准化。 “execv 在 C++ 存在之前采用了 char * 参数”——但在 C 存在之后(但在 C 标准化之前)。 const 不是“C++ 的东西”。它早在 C++ 出现之前就存在于 C 中。【参考方案3】:这只是 C / C++ 风格的 const 不能很好地工作的情况。实际上,内核不会修改传递给 exec() 的参数。它只是在创建新进程时复制它们。但是类型系统的表达能力不足以真正处理好这个问题。
这个页面上的很多人都建议让 exec 使用“char**”或“const char * const[]”。但这些都不适用于您的原始示例。 “char**” 意味着一切都是可变的(对于字符串常量“/usr/bin/whatever”来说肯定不是真的)。 “const char *const[]”意味着没有什么是可变的。但是你不能为数组的元素分配任何值,因为数组本身就是 const。
你能做的最好的就是有一个像这样的编译时 C 常量:
const char * const args[] =
"/usr/bin/whatever",
filename.c_str(),
someparameter.c_str(),
0;
这实际上适用于“const char *const[]”的建议类型签名。但是,如果您需要可变数量的参数怎么办?那么你不能有一个编译时常量,但你需要一个可变数组。所以你又开始捏造东西了。这就是 exec 的类型签名采用“const char **”作为参数的真正原因。
顺便说一句,C++ 中的问题是相同的。您不能将 std::vector 传递给需要 std::vector 的函数。您必须对整个 std::vector 进行类型转换或复制。
【讨论】:
【参考方案4】:我通常用以下方法破解它:
#define execve xexecve
#include <...>
#include <...>
#include <...>
#undef execve
// in case of c++
extern "C"
int execve(const char * filename, char ** argvs, char * const * envp);
;/
【讨论】:
不错,不好。最好使用系统的正确标头(以确保链接器引入的函数类型是您所期望的,假设 lib 和标头匹配)。使用const_cast<>
更干净地执行此操作,而不是通过使用预处理器以可怕的方式重新定义来覆盖所有内容。
我认为正确的 C++ 签名应该是 int execve(char const* filename, char const* const* argvs, char const* const* envp);以上是关于execv() 和 const-ness的主要内容,如果未能解决你的问题,请参考以下文章