【问题标题】:GCC compiler and converting const char* to char *GCC 编译器并将 const char* 转换为 char *
【发布时间】:2012-01-18 04:52:15
【问题描述】:

我正在尝试构建 M-SIM 架构模拟器,但是当我运行 make 实用程序时,gcc 会报告此错误(甚至不是警告)

注意:预期为“char *”,但参数类型为“const char *”

因为这被认为是一个错误。是否有任何标志可以绕过此检查?

【问题讨论】:

  • 您可能不应该忽略警告;不要将字符串文字传递给期望可修改缓冲区的函数
  • 在 gcc 的信息页面中搜索 -fno-const-strings,这与 Seth 的观点相呼应。
  • 向我们展示触发错误的代码。在 C 中,字符串文字不是const。并向我们​​展示(复制和粘贴)确切的错误信息;我不记得看到以“note”开头的 gcc 错误消息。
  • 更正:gcc 确实会产生该消息。看我的回答。
  • @keeto 对于 GCC 中的 C,您可以使用 -Wwrite-strings 为字符串文字提供“const char*”类型以产生警告。当您的任何代码或库代码被编译时,您是否打开了该警告?如果没有,GCC 不应该产生警告。作为对 honk 的回复,-fno-const-strings 是一个 C++ 选项,在 GCC 3.x 中已弃用并从 4.x 中删除。

标签: c gcc build literals


【解决方案1】:

这是一个错误,因为将 const char* 参数传递给采用 char* 参数的函数违反了 const 正确性;它将允许您修改 const 对象,这将破坏 const 的全部目的。

例如,这个 C 程序:

#include <stdio.h>

void func(char *s) {
    puts(s);
    s[0] = 'J';
}

int main(void) {
    const char message[] = "Hello";
    func(message);
    puts(message);
    return 0;
}

从 gcc 生成以下编译时诊断:

c.c: In function ‘main’:
c.c:10:5: warning: passing argument 1 of ‘func’ discards qualifiers from pointer target type
c.c:3:6: note: expected ‘char *’ but argument is of type ‘const char *’

最后一条消息被标记为“注释”,因为它引用了func() 的(完全合法的)声明,说明这是警告所指的参数声明。

就 C 标准而言,这是违反约束的,这意味着编译器可以将其视为致命错误。默认情况下,gcc 只会发出警告,并进行从 const char*char* 的隐式转换。

当我运行程序时,输出是:

Hello
Jello

这表明,即使我将message 声明为const,该函数也能够对其进行修改。

由于 gcc 没有将此视为致命错误,因此无需隐藏任何诊断消息。代码完全有可能无论如何都可以工作(例如,如果函数没有碰巧修改任何内容)。但警告的存在是有原因的,您或 M-SIM 架构模拟器的维护者可能应该看看这个。

(将字符串文字传递给func() 不会触发这些诊断,因为C 不会将字符串文字视为const。(它确实使尝试修改字符串文字的行为未定义。)这是由于历史原因。gcc 确实有一个选项 -Wwrite-strings,它会导致它将字符串文字视为 const;这实际上违反了 C 标准,但它可能是一个有用的检查。)

正如我在评论中提到的,如果您向我们展示触发诊断的代码将会很有帮助。

我什至自己下载并构建了 M-SIM 架构模拟器,但我没有看到该特定消息。

【讨论】:

  • 亲爱的 Keith,反之亦然,是否将 char 指针传递给期望 const char 指针的函数也违反约束?为什么不?还有案例编号。 2 如果我将 const char 指针传递给需要 char 指针的函数会发生什么 - 但该函数没有修改数据指针,是否也违反了约束?
  • @Giorgi:不,这不是违反约束,它完全有效。例如,strlen 接受 const char*,但您可以将指向可修改数组的 char* 传递给它。您可以根据需要修改数组,但 strlen 不会。
  • 所以将 char* 传递给期望 const char * 的函数是可以的,反之亦然?即使函数不会修改指向的数据
  • @Giorgi:是的。为什么不呢?您没有修改数据。
【解决方案2】:

指向const 限定类型的指针不会隐式转换为指向非const 限定类型的指针。必须通过强制转换进行显式转换,例如:

foo((char *)bar)

【讨论】:

  • 如果bar 实际上是一个常量字符指针,这可能会很危险。
  • 在大多数情况下需要修复代码,这样您就不必使用强制转换了。
  • 不确定 -1 的用途。我同意 OP 可能做错了什么,但从问题中无法判断,至少我没有做出虚假的假设(就像许多其他的ppl) 所讨论的表达式是一个字符串文字,指定了-fconst-strings...
【解决方案3】:

首先在函数调用(使用原型定义的函数)中,参数被转换为参数的类型,就像通过赋值一样。

您可以将char * 类型的值分配给const char * 类型的对象,但不能将const char * 值分配给char * 对象。

这个约束出现在赋值运算符的约束中:

(C99, 6.5.16.1p1) “以下其中一项应成立:[...] - 两个操作数都是指向兼容类型的合格或非合格版本的指针,左侧指向的类型具有所有限定符右边指向的类型;"

此约束允许第一次赋值,但不允许第二次赋值。

声明const char * 类型的指针意味着您不会修改指针指向的对象。所以你可以给指针赋值char *类型,这只是意味着对象不会被const char *指针修改。

但是声明char * 类型的指针意味着您可以修改指针指向的对象。给它赋值const char * 是没有意义的。

请记住,在 C 中,const 并不意味着 常量,而是 只读const 限定符放在指针类型之前意味着您承诺不会通过这些指针类型的对象修改对象。

【讨论】:

    【解决方案4】:

    如果您还没有,请执行以下步骤:

    1. 声明一个字符指针。
    2. 如有必要,分配空间并从常量字符串中复制内容。 (例如,通过 使用strdup())
    3. 并将常量 char 指针替换为新的 char 指针。

    【讨论】:

    • 而且,如果您使用strdup() 分配了空间,请不要忘记在完成后释放它。