如何在GCC中摆脱从字符串常量到'char *'的过时转换警告?


409

因此,我正在开发一个非常大的代码库,最近将其升级到gcc 4.3,现在会触发以下警告:

警告:不建议将字符串常量转换为'char *'

显然,解决此问题的正确方法是找到每个声明,例如

char *s = "constant string";

或函数调用,例如:

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

并使其成为const char指针。但是,这意味着最少要触摸564个文件,这不是我目前希望执行的任务。现在的问题是我正在使用-werror,因此我需要某种方式来抑制这些警告。我怎样才能做到这一点?


当您确实要解决替换554行时,sed是一个好朋友。但是请确保先备份。
马特

2
我查看了有关如何抑制错误消息以及应该进行正确替换的讨论。我对此没有任何意见。但是,我认为Matt的方向正确。定义要用什么替换的内容。您只需要正确的正则表达式即可。在副本中进行更改。使用“ diff”将它们与原始文件进行比较。使用sed进行更改是快速,轻松和免费的,而diff也是快速,轻松和免费的。尝试一下,看看您需要检查多少更改。发布您要用什么替换的内容,并让用户建议使用正则表达式替换。
托马斯·海登

整个讨论都遗漏了为什么这个问题完全需要根据gcc警告进行修复。原因是在David Schwartz的答案stackoverflow.com/questions/56522654/…中
andig

Answers:


227

我相信传递-Wno-write-strings给gcc将抑制此警告。


6
是否可以使用编译指示在每个文件上禁用它。
2009年

18
@PriyankBolia bdonlan评论了Rob Walker可以使用的答案#pragma GCC diagnostic ignored "-Wwrite-strings"
2013年

9
除了您控制API之外,在这种情况下,以下@John的答案有关更改签名以接受const char *更为正确。
jcwenger 2014年

215
这是糟糕的做法,很遗憾它获得了所有这些选票。警告不存在,因此您可以忽略它们。警告在那里告诉您“老兄,您正在做错了事,要小心”,只有在您想要像“闭嘴,我知道我在做什么”这样的回答时,才应该压制它们。婴儿程序员不是这种情况。
量子物理学家

9
我同意,您不应摆脱警告,而应使用约翰提供的解决方案。太糟糕了,这个是公认的答案!
杰罗姆

563

传递字符串文字的任何函数"I am a string literal"都应使用char const *类型代替char*

如果您要修复某些问题,请正确修复。

说明:

您不能使用字符串文字来初始化将被修改的字符串,因为它们的类型是const char*。放弃constness以后再修改它们是未定义的行为,因此您必须复制const char*字符串char通过char到动态分配的char*字符串,以对其进行修改。

例:

#include <iostream>

void print(char* ch);

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

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

25
确实如此,但您并不总是能够控制可能无法正确使用char */的第三方API const char *,因此在这种情况下,我通常会进行转换。
ideaman42'4

15
@ppumkin不幸的是,许多C标准库字符串函数都接受参数,char*即使对于不会被修改的字符串也是如此。如果您将参数设为a char const*并将其传递给带有a的标准函数,char*则会遇到问题。如果库函数不会处理该字符串,则可以丢弃const
2015年

仅仅因为并非总是可能,并不意味着它在很多常见的生产代码中多次都不是首选方法。
LovesTha

1
我现在完全了解该解决方案以及字符串文字的功能。但是也许其他人没有,所以我“保留”了解释的必要
NicoBerrogorry

1
我不明白如何应用您的解决方案:(
desmond13

69

我有一个类似的问题,我这样解决了:

#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',则常量字符串会破坏代码,对吗?但是无论如何这似乎无法回答问题,因为错误已经存在于旧代码中而不是新代码中,因此他仍然需要更改旧代码。
若昂·波特拉

我也是采用这种方法。如果有人正在搜索char **in 的情况,PyArg_ParseTupleAndKeywords我会执行以下操作:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
dashesy

@elcuco:我不确定C ++静态数组如何工作。这真的会复制任何数据,而不仅仅是指针吗?
亚历山大·马拉霍夫

尽管在某些情况下盲目采用此方法可能有好处,但IMO可能弊大于利。盲目地应用它可能导致指针悬空。它还将使用无意义的字符串副本来膨胀代码。
plugwash


30

如果它是活动的代码库,则您可能仍要升级代码库。当然,手动执行更改是不可行的,但我相信可以通过一个sed命令彻底解决此问题。不过,我还没有尝试过,所以下面加点盐。

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

这可能无法找到所有位置(甚至不考虑函数调用),但可以缓解问题,并可以手动执行一些剩余更改。


7
这并不仅解决了声明的警告,而不是函数调用+1 sed的福呢:P
若昂·波特拉

25

我不能使用编译器开关。所以我转过头了:

char *setf = tigetstr("setf");

对此:

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

1
+1-您不能更改应用程序的左值,只能更改右值。事实证明,这可以解决真正的问题。其他只是解决一些与编译器有关的问题。
elcuco

1
真正令人讨厌的是tigetstr()应该使用(const char *)而不是(char *)进行原型制作
vy32

2
当我这样做时,我得到的是“警告:从类型'const char *'强制转换为类型'char *'会强制转换常量”。我必须使用const_cast摆脱所有警告:const_cast <char *>(“ setf”)
CrouZ

2
我认为const cast是此页面上第一个可接受的解决方案(API更改除外)。
rwst 2015年

25

这是内联到文件中的方法,因此您无需修改​​Makefile。

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

然后您可以...

#pragma GCC diagnostic pop

25

更换

char *str = "hello";

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

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

foo("hello");

替换为

foo((char*) "hello");

15

代替:

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

这有效:

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

这是正确的方法,因为无论如何您都不应该将(常量)字符串传递给需要非常量字符串的函数!
jfla 2014年

15

在C ++中,使用const_cast如下所示

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

7

Test string是常量字符串。所以你可以这样解决:

char str[] = "Test string";

要么:

const char* str = "Test string";
printf(str);


2

从常量字符串到char指针进行类型转换,即

char *s = (char *) "constant string";

1

在C ++中,替换:

char *str = "hello";

与:

std::string str ("hello");

如果您想比较一下:

str.compare("HALLO");

1

我不明白如何应用您的解决方案:( – kalmanIsAGameChanger

使用Arduino Sketch,我有一个引起警告的函数。

原始函数:char StrContains(char * str,char * sfind)

为了停止警告,我在char * str和char * sfind前面添加了const

修改:char StrContains(const char * str,const char * sfind)。

所有警告都消失了。


按照警告的说法,这是正确的答案:“警告:不建议将字符串常量转换为'char *'”。
诺伯特·博罗斯

0

看到这种情况:

typedef struct tagPyTypeObject
{
    PyObject_HEAD;
    char *name;
    PrintFun print;
    AddFun add;
    HashFun hash;
} PyTypeObject;

PyTypeObject PyDict_Type=
{
    PyObject_HEAD_INIT(&PyType_Type),
    "dict",
    dict_print,
    0,
    0
};

监视名称字段,在gcc中会毫无警告地进行编译,但是在g ++中,它将不知道为什么。


gcc表示将该文件视为C源文件,g ++将该文件视为c ++源文件,除非被-x覆盖?选项。因此,不同的语言,c和c ++在警告内容方面存在细微的差异。
zhaorufei

0

您还可以通过调用,从字符串常量创建可写字符串strdup()

例如,此代码生成警告:

putenv("DEBUG=1");

但是,以下代码不会(将字符串传递给之前在堆上进行复制putenv):

putenv(strdup("DEBUG=1"));

在这种情况下(也许在其他大多数情况下),关闭警告是一个坏主意,它的存在是有原因的。另一种选择(默认情况下使所有字符串都可写)可能效率不高。

听听编译器告诉您的内容!


6
而且它还会泄漏为该可写字符串分配的内存。
RBerteig

1
是的,它确实是有目的的。如上所述,一次性(例如,初始化)代码没有问题。或者,您可以自己管理内存并在完成后释放它。
BillAtHRST

1
的特殊情况putenv()充满了烦恼-这不是一个很好的例子选择(至少,putenv()对于这个问题,没有什么更多的讨论)。这是一个单独的讨论。(请注意putenv(),基于POSIX定义之前的传统实现,有关POSIX行为的POSIX规范是有问题的。)IIRC,GNU C库的最新(此千年)版本中存在一个与putenv()行为更改有关的错误,并被换回。)
乔纳森·莱弗勒

0

只需为g ++使用-w选项

例:

g ++ -w -o simple.o simple.cpp -lpthread

请记住,这不会避免弃用,而是会阻止在终端上显示警告消息。

现在,如果您真的想避免过时,请使用const关键字,如下所示:

const char* s="constant string";  


0

现在的问题是我正在运行-Werror

IMO,这是您的真正问题。您可以尝试一些从(char *)转换为(const char *)的自动化方法,但是我会花钱在它们上,而不仅仅是工作。您将需要一名人员参与至少一些工作。在短期内,只需忽略该警告(但IMO将其保留为打开状态,否则它将永远无法修复),而只需删除-Werror。


9
人们之所以使用-Werror是,这样的警告得到固定。否则,它们永远不会被修复。
Zan Lynx 2010年

2
人们使用-Werror的原因是因为他们只从事玩具项目,或者自虐。当您的LOC超过100k时,由于GCC更新而使代码无法构建是一个真正的问题。迪托 有人在构建中添加了诸如“ -Wno-write-strings”之类的垃圾来摆脱恼人的警告(如这篇文章中评分最高的评论所示)。
James Antill 2010年

2
在该主题上有明显的分歧,例如,programmer.97things.oreilly.com
wiki / index.php /…–JoãoPortela 2010年

3
@James:您提出了一个有趣的观点,但是必须有一个更好的方法。似乎不立即修复警告没有意义-当您没有删除所有旧警告时如何识别新代码调用了新警告?根据我的经验,这只会导致人们无视他们不应该忽略的警告。
nobar 2011年

2
@James:我们的玩具项目是1.5 + M LOC(多语言)。正如nobar所说,-Werror避免忽略不应出现的警告,是的,每次出现新版本的编译器时,我们都必须重新检查所有内容。-Wno-write-strings仅在将Boost逐个文件地用于python包装器时使用,因为我们不打算重写Boost(目前,2017年,我们希望不再使用Boost而是C ++ 11 / cython)。然后必须通过质量检查定期检查每个忽略的警告,以查看是否现在可以通过代码避免它们,或者是否尚不可能。
msn

0

谢谢大家的帮助。从这里挑选到那里就是这个解决方案。这样编译干净。尚未测试代码。也许明天吧...

const char * timeServer[] = { "pool.ntp.org" }; // 0 - Worldwide 
#define WHICH_NTP            0 // Which NTP server name to use.
...
sendNTPpacket(const_cast<char*>(timeServer[WHICH_NTP])); // send an NTP packet to a server
...
void sendNTPpacket(char* address) { code }

我知道,timeServer数组中只有1个项目。但是可能还有更多。其余的已被注释掉以节省内存。


-1
PyTypeObject PyDict_Type=
{ ...

PyTypeObject PyDict_Type=
{
  PyObject_HEAD_INIT(&PyType_Type),
                     "dict",
                     dict_print,
                     0,
                     0
}; 

监视名称字段,在gcc中会毫无警告地进行编译,但是在g ++中,它将不知道为什么。

gcc (Compiling C),-Wno-write-strings默认情况下处于活动状态。

g++ (Compiling C++) -Wwrite串是默认激活

这就是为什么会有不同的行为的原因。对于我们来说,使用的宏会Boost_python生成此类警告。所以我们-Wno-write-strings在编译C ++时使用,因为我们总是使用-Werror


-1

const将字符串声明为a 将解决问题:

char const*s = "constant string";
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.