ein*_*ica 7 c c++ cross-language language-lawyer extern-c
假设我正在使用一些具有函数的 C 库:
int foo(char* str);
Run Code Online (Sandbox Code Playgroud)
我知道一个事实,即foo()不会修改str. 它只是写得不好,也懒得声明str为常量。
现在,在我的 C++ 代码中,我目前有:
extern "C" int foo(char* str);
Run Code Online (Sandbox Code Playgroud)
我像这样使用它:
foo(const_cast<char*>("Hello world"));
Run Code Online (Sandbox Code Playgroud)
我的问题:原则上,从语言律师的角度来看,在实践中,我这样写是否安全:
extern "C" int foo(const char* str);
Run Code Online (Sandbox Code Playgroud)
并跳过const_cast'ing?
如果没有安全,请解释原因。
注意:我对 C++98 代码的情况特别感兴趣(是的,不幸的是我),所以如果你假设语言标准的更新版本,请这么说。
我写: 并跳过 const_cast'ing 安全吗?
不。
如果不安全,请解释原因。
——从语言方面来说:
阅读dcl.link后,我认为 C 和 C++ 之间的互操作性如何工作并没有明确指定,有许多“不需要诊断”的情况。最重要的部分是:
具有相同函数名(忽略限定它的命名空间名称)的 C 语言链接函数的两个声明出现在不同的命名空间范围中,引用相同的函数。
因为它们引用相同的函数,所以我相信一个合理的假设是,C++ 端具有 C 语言链接的标识符的声明必须与 C 端该符号的声明兼容。在 C++ 中,没有“兼容类型”的概念,在 C++ 中,两个声明必须相同(转换后),这使得限制实际上更加严格。
从 C++ 方面,我们阅读c++draft basic#link-11:
在对类型进行所有调整后(在此期间 typedef 被其定义替换),引用给定变量或函数的所有声明指定的类型应相同,[...]
int foo(const char *str)由于C++ 翻译单元中带有 C 语言链接的声明与 C 翻译单元中声明的声明不同int foo(char *str)(因此它具有 C 语言链接),因此行为是未定义的(著名的“无需诊断”)。
从 C 方面(我认为这甚至不需要 - C++ 方面足以使程序具有未定义的行为。无论如何),最重要的部分是C99 6.7.5.3p15:
为了使两个函数类型兼容,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面一致;相应的参数应具有兼容的类型[...]
因为从C99 6.7.5.1p2开始:
为了使两个指针类型兼容,两者都应具有相同的限定,并且两者都应是指向兼容类型的指针。
为了使两个合格类型兼容,两者都应具有兼容类型的相同合格版本 [...]
所以因为char不兼容const char,所以const char *不兼容char *,所以int foo(const char *)不兼容int foo(char*)。调用这样的函数(C99 6.5.2.2p9)将是未定义的行为(您也可以看到C99 J.2)
——从实用的角度来说:
我不相信能够找到一种编译器+架构组合,其中一个翻译单元看到int foo(const char *)而另一个翻译单元定义了一个函数int foo(char *) { /* some stuff */ },并且它将“不起作用”。
从理论上讲,一个疯狂的实现可能会使用不同的寄存器来传递const char*参数,并使用不同的寄存器来传递char*参数,我希望这能够在那个疯狂的架构 ABI 和编译器中得到很好的记录。如果是这样,参数将使用错误的寄存器,它将“不起作用”。
尽管如此,使用一个简单的包装器不需要任何成本:
static inline int foo2(const char *var) {
return foo(static_cast<char*>(var));
}
Run Code Online (Sandbox Code Playgroud)