如何在GCC中消除“从字符串常量到'char *'的弃用转换”警告

428

我正在处理一个非常庞大的代码库,最近升级到GCC 4.3,现在会触发这个警告:

警告:从字符串常量转换为'char *'是过时的

显然,正确的修复方法是找到每个声明类似于

char *s = "constant string";

或者像以下这样调用函数:

void foo(char *s);
foo("constant string");

并将它们变成 const char 指针。然而,这将意味着至少要修改 564 个文件,而我现在不想执行这项任务。目前的问题是我正在使用 -Werror,因此我需要一种方法来消除这些警告。我该怎么做?


2
当你开始处理替换554行时,sed是一个好帮手。但是请确保首先备份。 - hookenz
2
我看了关于如何抑制错误信息以及正确替换的讨论。对此我没有任何意见。然而,我认为Matt是正确的。定义你想要替换的内容。你只需要正确的正则表达式。在副本中进行更改。使用“diff”将其与原始文件进行比较。使用sed进行更改快速、简单且免费,diff也是快速、简单且免费的。尝试一下,看看你需要审核多少更改。发布你想要替换的内容,并让用户建议正则表达式替换。 - Thomas Hedden
整个讨论都忽略了gcc警告提示需要修复的问题的本质原因。原因在于David Schwartz在https://stackoverflow.com/questions/56522654/why-the-warining-deprecated-conversion-from-string-constant-to-char-occured-i中进行了解释。 - andig
564个文件完全可以做到。就这么干吧。(嗯,很可能你现在已经完成了它;-))。 - Peter - Reinstate Monica
23个回答

583

任何传递字符串字面量 "I am a string literal" 的函数应该使用 char const * 作为类型,而不是 char*

如果你要修复某些东西,就要把它修好。

Explanation:

您不能使用字符串字面量来初始化会被修改的字符串,因为它们的类型是const char*。强制转换去掉constness以后再去修改是未定义的行为,因此您必须将const char*字符串逐个字符地复制到动态分配的char*字符串中以便修改它们。

Example:

#include <iostream>

void print(char* ch);

void print(const char* ch) {
    std::cout<<ch;
}

int main() {
    print("Hello");
    return 0;
}

27
尽管这是正确的,但你并不总是能够控制第三方API的使用,他们可能没有正确地使用 char * / const char *,因此在这种情况下,我通常会进行强制转换。 - ideasman42
17
很遗憾,许多 C 标准库的字符串函数即使对于不会被修改的字符串也将参数作为 char* 类型处理。如果你将参数作为 char const* 并传递给接受 char* 的标准函数,则会出现问题。如果库函数不会操作该字符串,则可以去除 const - John
仅仅因为并非总是可行,并不意味着在常见的生产代码中出现这种警告时它不是首选选项。 - LovesTha
1
我现在完全理解了解决方案和字符串字面值的功能。但是也许其他人不理解,所以我“保留”需要解释的需求。 - NicoBerrogorry
1
我不明白如何应用你的解决方案 :( - desmond13
显示剩余2条评论

240

6
可以使用编译指示符在每个文件层面上禁用它。 - Priyank Bolia
21
@PriyankBolia和bdonlan在评论中提到可以使用#pragma GCC diagnostic ignored "-Wwrite-strings"来解决问题。 - MasterMastic
9
除非您控制API,否则@John所提供的答案(更改签名以接受const char *)才是更正确的。 - jcwenger
229
这是极其糟糕的做法,我很难过它得到了那么多票。警告存在并不是让你忽略它们,而是在告诉你:“兄弟,你正在做一些可能是错误的事情,请小心”,只有当你想回应“闭嘴,我知道我在做什么”的时候才应该禁止警告,而这很可能不适用于初学者程序员。 - The Quantum Physicist
11
我同意,你不应该去除警告,而是应该使用John提供的解决方案。可惜这个被接受为答案了! - Jérôme
显示剩余5条评论

75

我遇到了类似的问题,我是这样解决的:

#include <string.h>

extern void foo(char* m);
 
int main() {
    // warning: deprecated conversion from string constant to ‘char*’
    //foo("Hello");
   
    // no more warning
    char msg[] = "Hello";
    foo(msg);
}

我无法访问foo以便将其调整为接受 const char*,这将是更好的解决方案,因为foo没有改变m


8
@elcuco,你有什么建议?我无法编辑foo,并尝试寻找不需要抑制警告的解决方案。在我的情况下,后者更多是一种锻炼的方式,但对于原始发布者来说,似乎很重要。据我所知,我的回答是唯一能同时解决我的和OP的条件的答案,因此它可能对某些人是有价值的。如果你认为我的解决方案不够好,请提供另一种选择。(不包括编辑foo或忽略警告的方案。) - BlackShift
如果我们假设foo已经被正确编码(不幸的是,对于'Josh Matthews'所谈论的代码似乎并非如此),那么这就是最佳解决方案。这是因为如果函数需要实际更改字符串'msg',传递一个常量字符串将会破坏代码,对吧?但无论如何,这似乎并没有回答问题,因为错误已经存在于旧代码中,而不是新代码,所以他仍然需要更改旧代码。 - João Portela
这也是我采用的方法。如果有人在PyArg_ParseTupleAndKeywords函数中搜索char **类型的情况,我会这样做:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL}; - dashesy
@elcuco: 我不确定 C++ 静态数组是如何工作的。这是否会真正复制任何数据,而不仅仅是指针? - Alexander Malakhov
2
尽管这种方法在某些情况下可能有优点,但盲目应用它可能会带来更多的伤害。盲目应用它可能很容易导致悬空指针。它还会使代码膨胀,增加无意义的字符串复制。 - plugwash
在对字符串进行简单函数操作的紧密循环中,性能惩罚可能是无法接受的。 - Peter - Reinstate Monica

68

54
确实会起作用:#pragma GCC diagnostic ignored "-Wwrite-strings"。 (注:该指令为C/C++中用于忽略特定编译器警告的命令,此处忽略了"-Wwrite-strings"警告,该警告通常出现在试图修改字符串字面值时) - bdonlan
1
这个答案实际上并没有包含答案。 - Asteroids With Wings
那个来自2011年的 Red Hat 链接已经(实际上)失效了(重定向到一个通用页面)。 - Peter Mortensen

34

如果这是一个活跃的代码库,你可能仍然想要升级代码库。当然,手动执行更改是不可行的,但我相信这个问题可以通过一条单独的 sed 命令一劳永逸地解决。尽管如此,我还没有尝试过,因此请持怀疑态度。

find . -exec sed -E -i .backup -n \
    -e 's/char\s*\*\s*(\w+)\s*= "/char const* \1 = "/g' {} \;

这可能无法找到所有地方(甚至不考虑函数调用),但它将减轻问题并使其可能手动执行一些剩余的更改。


8
只解决声明警告而不解决函数调用,顶着sed fu的头衔还是+1吧 :p - João Portela

28

以下是如何在文件中以行内方式执行此操作,因此您无需修改Makefile。

// gets rid of annoying "deprecated conversion from string constant blah blah" warning
#pragma GCC diagnostic ignored "-Wwrite-strings"

您随后可以...

#pragma GCC diagnostic pop

26

替换

char *str = "hello";

char *str = (char*)"hello";

或者如果您正在调用函数:

foo("hello");

用以下内容替换

foo((char*) "hello");

25

我无法使用编译器开关。所以我将其改为:

char *setf = tigetstr("setf");

变成这样:

char *setf = tigetstr((char *)"setf");

1
+1 - 你不能改变应用程序的左值,只能改变右值。这证明了解决了真正的问题。其他只是绕过编译器的一些问题。 - elcuco
2
真正让人烦恼的是,tigetstr() 应该使用 (const char *) 而不是 (char *) 进行原型声明。 - vy32
3
当我这样做时,会出现“警告:从类型'const char *'到类型'char *'的转换会消除const属性”的警告。我必须使用const_cast来消除所有警告:const_cast<char*>("setf")。 - CrouZ
2
我认为const cast是这个页面上第一个可接受的解决方案(除了API更改)。 - rwst

15

在C++中,使用如下方式使用const_cast

char* str = const_cast<char*>("Test string");

15

改为:

void foo(char *s);
foo("constant string");

这个有效:

void foo(const char s[]);
foo("constant string");

这是正确的做法,因为你不应该将一个(常量)字符串传递给一个期望非常量字符串的函数! - jfla

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