为什么将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**