在外部"C"声明中"玩"参数恒常性是否安全?



假设我使用的是某个C库,它有一个函数:

int foo(char* str);

并且我知道CCD_ 1不修改CCD_。它只是写得不好,并且没有麻烦声明str是常量。

现在,在我的C++代码中,我目前有:

extern "C" int foo(char* str);

我是这样使用的:

foo(const_cast<char*>("Hello world"));

我的问题是:原则上,从语言律师的角度来看,在实践中,我写安全吗

extern "C" int foo(const char* str);

跳过const_cast

如果不安全,请解释原因。

注意:我对C++98代码的情况特别感兴趣(是的,我很难过),所以如果你假设的是语言标准的更高版本,请这样说。

我写:并跳过const_cast‘ing安全吗?

否。

如果不安全,请解释原因。

来自语言端:

在阅读了dcl.link之后,我认为C和C++之间的互操作性是如何工作的并没有得到确切的说明;不需要诊断";案例。最重要的部分是:

具有C语言链接的函数的两个声明,具有相同的函数名称(忽略限定它的命名空间名称),出现在不同的命名空间作用域中,它们引用了相同的函数。

因为它们引用了相同的函数,我相信一个合理的假设是,在C++端具有C语言链接的标识符的声明必须与在C端的该符号的声明兼容。在C++中存在没有";兼容类型";,在C++中,两个声明必须相同(转换后),这使得限制实际上更加严格。

从C++方面,我们阅读了C++基本草案#link-11:

在对类型进行所有调整后(在此期间,typedef被其定义所取代),所有引用给定变量或函数的声明所指定的类型应相同,[…]

因为C++翻译单元中具有C语言链接的声明int foo(const char *str)与C翻译单元中声明的声明int foo(char *str)不相同(因此它具有C语言连接),所以行为是未定义的(著名的"不需要诊断")。

从C端(我认为这甚至是不需要的-C++端足以使程序具有未定义的行为。无论如何),最重要的部分将是C99 6.7.5.3p15:

对于要兼容的两种函数类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面达成一致;相应的参数应具有兼容类型[…]

因为C99 6.7.5.1p2:

对于要兼容的两个指针类型,两者都应具有相同的限定,并且都应是指向兼容类型的指针。

和C99 6.7.3p9:

对于两个合格的兼容类型,两者都应具有兼容类型的相同合格版本[…]

因此,由于charconst char不兼容,因此const char *foo()0不兼容,从而int foo(const char *)int foo(char*)不兼容。调用这样的函数(C99 6.5.2.2p9)将是未定义的行为(也可以参见C99 J.2)

--从实用角度来看:

我不相信能够找到编译器+体系结构的组合,其中一个翻译单元看到CCD_;不工作";。

从理论上讲,一个疯狂的实现可能会使用不同的寄存器来传递const char*参数,而使用另一个寄存器来传递char*参数,我希望这会在疯狂的体系结构ABI和编译器中得到很好的记录。如果是这样的话,错误的寄存器将被用于参数,它将";不工作";。

尽管如此,使用一个简单的包装器并不需要任何费用:

static inline int foo2(const char *var) {
return foo(static_cast<char*>(var));
}

我认为基本答案是:

是的,如果引用的对象本身是const(例如本例中的字符串文字),则可以丢弃const,甚至。未定义的行为仅指定在尝试修改const对象时出现,而不是由于强制转换。这些规则及其存在的理由是"古老的"。我确信它们早于C++98。

str0相比,在volatile中,通过非易失性引用访问易失性对象的任何尝试都是未定义的行为。我只能在此处将"access"读作read和/或write。

我不会重复其他建议,但这里是最偏执的解决方案。这是偏执的,并不是因为C++语义不清楚。他们很清楚。至少,如果你接受了一些未定义的行为,那就很清楚了!

但你形容它"写得很糟糕",你想在它周围放一些沙袋!

偏执的解决方案依赖于这样一个事实,即如果你传递一个常量对象,它在整个执行过程中都是常量(如果程序不冒UB的风险)。

因此,制作一份";你好世界";在调用堆栈中更低,甚至初始化为文件范围对象。您可以在函数中声明它为static,并且它将只构造一次(开销最小)。

这几乎恢复了字符串文字的所有优点。包括文件作用域(全局)在内的调用堆栈越低越好。我不知道传递给foo()的指向对象的生存期需要多长。因此,它至少需要在链中足够低才能满足这个条件。注意:C++98有std::string,但它在这里不太管用,因为你仍然被禁止修改c_str()的结果。这里定义了语义。

#include <cstring>
#include <iostream>
class pseudo_const{
public:
pseudo_const(const char*const cstr): str(NULL){
const size_t sz=strlen(cstr)+1;
str=new char[sz];
memcpy(str,cstr,sz);
}

//Returns a pointer to a life-time permanent copy of 
//the string passed to the constructor.
//Modifying the string through this value will be reflected in all
// subsequent calls.  
char* get_constlike() const {
return str;
}

~pseudo_const(){
delete [] str;
}
private:
char* str;
};
const pseudo_const str("hello world");
int main() {
std::cout << str.get_constlike() << std::endl;
return 0;
}

相关内容

  • 没有找到相关文章

最新更新