GCC编译器和将const char*转换为char*

5

我正在尝试构建M-SIM架构模拟器,但是当我运行make工具时,gcc报告了这个错误(甚至不是一个警告)

注意:期望的是'char *'类型的参数,但实际传入的是'const char *'类型。

为什么现在这被视为错误了呢?有没有任何标志可以绕过此检查?


4
最好不要忽视这个警告;如果一个函数期望传入可修改的缓冲区,就不要传入一个字符串字面量。 - Seth Carnegie
2
请展示引发错误的代码。在C语言中,字符串字面量并不是const类型。同时,请向我们展示(复制粘贴)完整的错误信息;我记得gcc的错误信息不应该以“note”开头。 - Keith Thompson
1
更正:gcc确实会产生那个消息。请看我的回答。 - Keith Thompson
@zneak 是的,因为字符串字面量不需要可修改。在 C 中修改字符串字面量是未定义的行为。 - ouah
1
@keeto 对于GCC中的C语言,你可以使用-Wwrite-strings选项将字符串字面值的类型设置为"const char*"以产生警告。在编译你的代码或库代码时,是否打开了该警告?如果没有,GCC就不应该产生警告。回应honk的问题,-fno-const-strings是GCC 3.x中弃用的C++选项,并在4.x中被移除了。 - SO Stinks
显示剩余4条评论
4个回答

10

这是一个错误,因为将一个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 *’
最终的消息被标记为"note",因为它提到了(完全合法的)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 指针的函数也违反了约束吗?为什么不是呢?还有第二种情况,如果我将 const char 指针传递给期望 char 指针的函数——但该函数不修改指针所指向的数据,这也会违反约束吗? - Giorgi Moniava
@Giorgi:不,这不是约束违规,而且它是完全有效的。例如,strlen接受一个const char*,但你可以传递一个指向可修改数组的char*。你可以随意修改数组,但strlen不会修改它。 - Keith Thompson
将char传递给期望const char的函数是可以的,但反过来则不行吗?即使函数不会修改所指向的数据。 - Giorgi Moniava
@Giorgi:是的。为什么不可以呢?你不一定要修改数据。 - Keith Thompson

5

const修饰的类型的指针不能隐式转换成非const修饰的类型的指针。必须通过显式转换来进行,例如:

代码示例:
const int *p;
int *q = const_cast(p);
foo((char *)bar)

1
如果bar实际上是一个常量字符指针,那么这可能是危险的。 - shinkou
在大多数情况下,必要的是修复代码,这样就不必使用强制转换。 - Keith Thompson
不确定那些-1是用来干什么的。我同意OP可能正在做错什么,但是从问题中无法判断,至少我没有像许多其他人一样做出虚假的假设,即所讨论的表达式是一个字符串常量,并指定了“-fconst-strings”。 - R.. GitHub STOP HELPING ICE

4

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

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

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

(C99, 6.5.16.1p1) "应满足以下条件之一:[...] - 两个操作数都是指向限定或未限定版本的兼容类型的指针,并且左边指向的类型具有右边指向的类型的所有限定符;"

这个约束允许第一个赋值,但禁止第二个。

使用类型const char *声明指针意味着您不会修改指针所指向的对象。因此,您可以将指针分配为char *类型的值,这只意味着对象不会通过const char *指针进行修改。

但是,声明类型为char *的指针意味着您可以修改指针所指向的对象。将其分配为const char *的值是没有意义的。

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


0
如果您还没有,请执行以下步骤:
  1. 声明一个字符指针。
  2. 如果需要,分配空间并从常量字符串复制内容。(例如使用 strdup()
  3. 用新的字符指针替换常量字符指针。

而且,如果你使用strdup()分配了空间,不要忘记在使用完后将其释放。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接