【问题标题】:Why does passing char** as const char** generate a warning?为什么将 char** 作为 const char** 传递会产生警告?
【发布时间】:2013-01-28 13:14:26
【问题描述】:

我收到了这个警告:

note: expected ‘const char **’ but argument is of type ‘char **’

现在,我通过将参数转换为 const char ** 来传递参数。有没有其他方法可以摆脱它?

【问题讨论】:

  • 能否提供您正在使用的函数原型?
  • 当然。 int CompareRecords(const char **, const char **, const int *, const int)
  • 和你正在使用的函数的代码

标签: c gcc


【解决方案1】:

简答

你能安全地将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 小时做了一个扎实的工作(实际上我在写这个答案之前赞成这个答案)。然后我认为他错了,同时他认为他的答案不适用。结果我们在那个飞跃中错了,因为他第一次是对的,我第二次错了,现在......呃。

在 VS2012 和 VS2010 上,标记的行都会在没有强制转换的情况下标记错误。 clang 将在 C 中使用警告编译它,但允许它(我发现这令人惊讶)。鉴于,你确实必须真正走出你快乐的地方才能打破它,但它仍然是破碎的。

剩下的部分是关于识别指针类型、它们的常量以及什么等价于什么的谩骂。


指针和常量的长篇大论

警告是因为char **const char ** 不等价(呃)。正确地说,您可以修复原型(被调用者),或修复调用者(通过加载 const char * 数组并传递它)。但是你能安全地将第一个转换为第二个吗?嗯……

请记住,按照标准,const 会立即转到其左侧的项目。在数据类型的最左侧声明它是该语言支持的一个优点,但通常会带来混乱或问题。作为一个经验法则,如果const 出现在紧靠类型之前的 decl 的最左侧,则它适用于数据 type不是后续指针(如果有)。当它出现在任何东西的右侧时,它适用于立即左侧的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 编译,则会出现错误)。在 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 ** 类型的参数会丢弃嵌套指针类型中的限定符。

VS2010 和 VS2012 都在另一方面,抛出一个错误:

错误 C2440:“正在初始化”:无法从“char **”转换为“const char **”

看起来很奇怪,但实际上 VS 更正确(奇迹永远不会停止)。

这很有意义。在类型声明中隐藏的事实是,其中第一个不允许修改最终数据,第二个允许。从上面我们知道char **const char **(又名char const **)是相同的。一个的底部是指向const char 的指针,而另一个是指向char 的指针。

【讨论】:

  • 请注意,转换为 char const * const * 是安全的(因为您无法更改它所指向的内容)。 (我已经使用 OCaml 的类型系统验证了这一点。)不幸的是,GCC 人似乎使用了大锤方法来禁止所有此类强制转换,而 Clang 似乎复制了它们。
  • @ChrisK 谢谢,克里斯。在重读了三个月前我现在写的内容之后,我不敢相信我把它遗漏了(但我包括了最后一个例子,这与将基指针设为 const 不同,duh)。我将尝试更新答案以反映该添加。再次感谢。编辑:我没有遗漏它。它就在那里,但你在安全方面的观点似乎是准确的。
【解决方案2】:

编辑:我什至回答错了问题。我的回答完全无关紧要!请忽略我。

edit 2:在这位绅士提问者澄清了他的问题之后,事实证明我的回答实际上是相关的。 C'est la vie.

这是一个有趣的 C 语言,如果你足够认真地思考它,它是有道理的。

基本上,转换:

char** ptr;
const char** const_ptr;
const_ptr = ptr;  // <-- BAD!

不允许。

为什么,你可能会问? “我让事情更多 const!这显然是件好事!”


好吧,考虑一下。如果允许,那么:

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 你死定了。所以...不:-)

【讨论】:

  • 如果不相关,您可以删除您的答案。
  • +1(很久以前,但值得一提)。这是最终使我工作的答案,如果普通读者还没有这样做,那么它当然值得一票。
【解决方案3】:

警告告诉您,您正在调用的函数期望给定参数为const char**,但您传递的是char** 参数。要摆脱这个警告,你可以

  • 真的传入const char**
  • 将您的参数转换为const char**(就像您目前正在做的那样)
  • 更改函数原型,使函数需要char**

【讨论】:

  • 作为第三种选择,如果函数不需要更改字符,我最好将函数原型更改为const char * const *
【解决方案4】:

我仍然认为这是不对的。在示例中:

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.

打破这一行的是:

*const_ptr = &c;

因为我们将一个非 const 子指针设置为一个 const 指针。如果这是哪里:

const char *const *const_ptr

然后

*const_ptr = &c 

会是正确的,它不会让你分配给 const char c。

我认为编译器不应该阻止你做一些更 const 的东西。

【讨论】:

    猜你喜欢
    • 2014-09-03
    • 2018-08-07
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多