为什么将char **作为const char **传递会产生警告?
我一直在收到这个警告:
note: expected 'const char **' but argument is of type 'char **'
现在,我通过将它们转换为const char **
来传递参数。 还有其他方法我可以摆脱它吗?
简答
你能安全地将char **
强制转换为const char**
吗? 不 。 (反正不安全),原因比你想象的要微妙得多。 你能用另一种方式摆脱它吗? 当然。 从char*
值加载一个const char*
值数组并传递它。 (或改变被调用者原型,但那就是作弊= P)。
考虑以下代码, 除了调用函数之外 ,它基本上执行您希望的所有内容。 标记的线表示等效的投射点
const char *s = "Test"; char *p = NULL; char **pp = &p; // Put address of our pointer in our pointer-to-pointer. const char **cpp = pp; // Here: assigning char** to const char** *cpp = s; // perfectly legal; pp and s both finish "char const" *p = 0; // ru ro raggy
真的盯着这需要一段时间,而且我承认起初我也没有看到它。 @sheu做了大约24小时才能抓住它,我真的想到它已经足够长时间才能意识到他一直都是对的(我在写这篇文章之前我真的赞成了这个答案)。 然后我认为他错了,同时他认为他的答案不适用。 事实certificate我们在那次飞跃中都错了,因为他第一次是对的,第二次我错了,现在……呃。
在VS2012和VS2010上,标记的行都将标记错误而不进行强制转换。 clang会在C中用警告编译它,但是允许它(我发现它很令人惊讶)。 鉴于,你必须真正走出你的幸福之地来打破它,但它仍然是非常少的。
剩下的就是识别指针类型,它们的常量以及等同于什么的指示。
指针与Const的长期诽谤
警告是因为char **
和const char **
不等同(duh)。 为了正确,你可以修复原型(被调用者),或修复调用者(通过加载一个const char *
数组并传递它)。 但是你可以安全地将第一个转换为第二个吗? 嗯….
请记住,标准const
会立即转到左侧的项目。 在数据类型的最左边声明它是语言支持的精确,但经常引入混淆或问题。 根据经验,如果const
出现在紧靠类型之前的十进制的最左边,则它适用于数据类型 ; 不是后续指针(如果有的话)。 当它出现在任何东西的右边时,它适用于立即左侧的decl-part,无论是数据类型部分还是指针部分,无论它只适用于单个部分。
以下是大量样本:
没有间接 :
const char ch; // const character. must be initialized. char const ch; // same as above
单方向 :
char *p; // p is mutable, *p is mutable const char *p; // p is mutable, *p is const char const *p; // same as above. char *const p; // p is const, *p is mutable, must be initialized. char const *const p; // p is const, *p is const, must be initialized.
双间接 :
char **p; // ptr-to-ptr-to-char // p, *p, and **p are ALL mutable const char **p; // ptr-to-ptr-to-const-char // p and *p are mutable, **p is const char const **p; // same as above char *const *p; // ptr-to-const-ptr-to-char // p is mutable, *p is const, **p is mutable. char **const p; // const-ptr-to-ptr-to-char // p is const, *p is mutable, **p is mutable. // must be initialized. const char **const p; // const-ptr-to-ptr-to-const-char // p is const, *p is mutable, **p is const. // must be initialized. char const **const p; // same as above char const *const *p; // ptr-to-const-ptr-to-const-char // p is mutable, *p is const, **p is const. const char *const *p; // same as above. char *const *const p; // const-ptr-to-const-ptr-to-char // p is const, *p is const, **p is mutable. // must be initialized.
当然谁不能离开家…
char const *const *const p; // const-ptr-to-const-ptr-to-const-char // everything is const. // must be initialized. const char *const *const p; // same as above
那么这对您的问题有何影响? 在C中编译该代码时,如果没有-Werror
您将收到编译器警告(如果使用-Werror
编译, -Werror
)。 在C ++中进行编译时,由于参数签名不匹配,因此只会出现明显的错误。 但为什么?
因为这些没有直接的等价:
const char **p; // ptr-to-ptr-to-const-char // p and *p are mutable **p is const char **p; // ptr-to-ptr-to-char // p, *p, and **p are all mutable
使用clang进行编译时,C中的确切警告如下:
main.c:15:9:将
char **
传递给const char **
类型的参数const char **
丢弃嵌套指针类型中的限定符。
另一方面,VS2010和VS2012都抛出一个错误:
错误C2440:’初始化’:无法从’char **’转换为’const char **’
这看起来很奇怪,但VS实际上更正确(奇迹永远不会停止)。
这很有道理。 紧跟在类型声明中的事实是,第一个不允许修改最终数据,第二个确实如此 。 从上面我们知道char **
和const char **
(又名char const **
)是不一样的。 在一个的底部是一个指向const char
的指针,而另一个是指向char
的指针。
编辑 :我甚至回答了错误的问题。 我的回答完全无关紧要! 请不要理我
编辑2 :在绅士提问者澄清他的问题之后,事实certificate我的回答实际上是相关的。 这就是生活。
这是一个有趣的C,如果你认真思考它就有意义。
基本上,转换:
char** ptr; const char** const_ptr; const_ptr = ptr; // <-- BAD!
不被允许。
为什么,你可能会问? “我正在制作更多的东西!这显然是件好事!”
好吧,想一想。 如果允许,那么:
const char c = 'A'; char* ptr; const char** const_ptr = &ptr; // <-- ILLEGAL, but what if this were legal? *const_ptr = &c; *ptr = 'B'; // <- you just assigned to "const char c" above.
BAM你死了。 所以不行 :-)
警告告诉您正在调用的函数将给定参数作为const char**
但您传递的是char**
参数。 要摆脱这个警告,你可以
- 真的传入一个
const char**
- 将您的参数转换为
const char**
(就像您目前正在做的那样) - 更改函数原型,以便函数需要一个
char**