如何摆脱 GCC 中“从字符串常量到‘char*’的不推荐转换”警告?

Posted

技术标签:

【中文标题】如何摆脱 GCC 中“从字符串常量到‘char*’的不推荐转换”警告?【英文标题】:How to get rid of `deprecated conversion from string constant to ‘char*’` warnings in GCC? 【发布时间】:2010-09-08 17:58:45 【问题描述】:

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

警告:不推荐将字符串常量转换为‘char*’

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

char *s = "constant string";

或函数调用如:

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

并将它们设为const char 指针。但是,这意味着至少要接触 564 个文件,这不是我目前希望执行的任务。现在的问题是我正在使用-werror,所以我需要一些方法来扼杀这些警告。我该怎么做?

【问题讨论】:

当你来解决替换 554 行时,sed 是一个好朋友。不过请务必先备份。 我查看了有关如何抑制错误消息以及正确替换应该是什么的讨论。我对此没有任何意见。但是,我认为马特是在正确的轨道上。定义要替换的内容。您只需要正确的正则表达式。在副本中进行更改。使用“差异”将它们与原始文件进行比较。使用 sed 进行更改快速、简单且免费,而 diff 也快速、简单且免费。试一试,看看您需要查看多少更改。发布您想要替换的内容,并让用户建议正则表达式替换。 整个讨论都忽略了为什么这是一个需要根据gcc警告解决的问题。原因在于 David Schwartz 的回答 ***.com/questions/56522654/…。 564 个文件是完全可行的。去做就对了。 (好吧,你现在很可能已经做到了;-))。 【参考方案1】:

string constants 传递给函数时,将其写为:

void setpart(const char name[]);

setpart("Hello");

除了const char name[],你也可以写const char \*name

消除此错误对我有用:

[Warning] deprecated conversion from string constant to 'char*' [-Wwrite-strings]

【讨论】:

【参考方案2】:

您向其中传递字符串文字 "I am a string literal" 的任何函数都应使用 char const * 而不是 char* 作为类型。

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

说明:

您不能使用字符串文字来初始化将被修改的字符串,因为它们的类型是const char*。抛弃常量以稍后修改它们是undefined behaviour,因此您必须通过charconst char* 字符串char 复制到动态分配的char* 字符串中才能修改它们。

示例:

#include <iostream>

void print(char* ch);

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


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

【讨论】:

虽然这是真的,但您并不总是能够控制可能无法正确使用 char * / const char * 的第 3 方 API,所以在这种情况下,我通常会进行转换。 @ppumkin 不幸的是,许多 C 标准库字符串函数将参数作为 char*,即使对于不会被修改的字符串也是如此。如果您将参数作为char const* 并将其传递给采用char* 的标准函数,那么您将成功。如果库函数不会处理字符串,您可以丢弃const 仅仅因为它并不总是可能的并不意味着它不是很多时候此警告出现在常见生产代码中的首选选项。 我现在完全理解了解决方案,以及字符串文字的功能。但也许其他人没有,所以我“认为”需要解释 我不明白如何应用您的解决方案:(【参考方案3】:

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

使用 Arduino Sketch,我有一个函数会导致我的警告。

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

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

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

所有警告都消失了。

【讨论】:

这是正确答案,因为警告说:“警告:不推荐使用从字符串常量到‘char*’的转换”。【参考方案4】:

在 C++ 中,替换:

char *str = "hello";

与:

std::string str ("hello");

如果你想比较一下:

str.compare("HALLO");

【讨论】:

【参考方案5】:

查看 gcc 的 Diagnostic Pragma 支持,以及 -W warning options 的列表(更改为:new link to warning options)。

对于 gcc,您可以使用 #pragma warning 指令,如解释 here。

【讨论】:

确实如此:#pragma GCC 诊断忽略“-Wwrite-strings” 这个答案实际上并不包含答案。【参考方案6】:

谢谢大家的帮助。从这里和那里挑选这个解决方案。这编译干净。还没有测试代码。明天……也许……

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 数组中只有一项。但可能还有更多。其余部分暂时被注释掉以节省内存。

【讨论】:

【参考方案7】:

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

这是你真正的问题,IMO。您可以尝试一些从 (char *) 移动到 (const char *) 的自动化方法,但我会为它们投入资金,而不仅仅是工作。您必须至少有人参与其中的一些工作。 在短期内,只需忽略警告(但 IMO 将其保持打开状态,否则将永远无法修复)并删除 -Werror。

【讨论】:

人们使用 -Werror 的原因是警告 do 得到修复。否则他们永远不会得到修复。 人们使用 -Werror 的原因是因为他们只从事玩具项目,或者他们是自虐狂。当您拥有 100k+ LOC 时,由于 GCC 更新而无法构建代码是一个真正的问题。迪托。有人在构建中添加诸如“-Wno-write-strings”之类的垃圾以摆脱烦人的警告(就像这篇文章中评价最高的评论所暗示的那样)。 该话题存在明显分歧,例如programmer.97things.oreilly.com/wiki/index.php/… @James:你提出了一个有趣的观点,但必须有更好的方法。不立即修复警告似乎毫无意义——当你没有删除所有旧警告时,你如何识别新代码何时调用了新警告?根据我的经验,这只会导致人们忽视他们不应该忽视的警告。 @James:我们的玩具项目是 1.5+M LOC(多语言)。正如 nobar 所说, -Werror 避免忽略不应该出现的警告,是的,每次编译器的新版本出现时,我们都必须重新检查所有内容。 -Wno-write-strings 仅在将 Boost 用于逐个文件的 python 包装器时使用,因为我们不打算重写 Boost(现在,2017 年,我们更愿意不再使用 Boost,而是 C++11/赛通)。然后必须通过质量检查定期检查每个被忽略的警告,看看它们现在是否可以通过代码避免,或者是否还不可能。【参考方案8】:

替换

char *str = "hello";

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

或者如果你在函数中调用:

foo("hello");

替换为

foo((char*) "hello");

【讨论】:

【参考方案9】:

做从常量字符串到字符指针的类型转换,即

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

【讨论】:

【参考方案10】:

为什么不直接使用类型转换?

(char*) "test"

【讨论】:

当指针是 const 时,它指向的东西是你不应该(或不能)改变的。将其转换为非常量允许代码(尝试)更改它。花几天时间找出原因总是很有趣,例如命令停止工作,然后发现有东西修改了比较中使用的 const 关键字。【参考方案11】:

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

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

【讨论】:

【参考方案12】:

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

#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)。

【讨论】:

@elcuco,你有什么建议?我无法编辑 foo,并试图找到不需要抑制警告的解决方案。就我而言,后者更像是一个锻炼问题,但对于原始海报来说,这似乎很重要。据我所知,我的答案是唯一可以同时解决我和 OP 条件的答案,因此它可能是对某人有价值的答案。如果您认为我的解决方案不够好,您能否提供替代方案? (这不包括编辑 foo 或忽略警告。) 如果我们假设 foo 被正确编码(不幸的是,'Josh Matthews' 正在谈论的代码似乎并非如此)这是最好的解决方案。那是因为如果函数需要实际更改字符串“msg”,传递给它一个常量字符串会破坏代码,对吗?但无论如何这似乎并没有回答这个问题,因为错误已经在旧代码中而不是在新代码中,所以他无论如何都需要更改旧代码。 这也是我采用的方法。如果有人在PyArg_ParseTupleAndKeywords 中搜索char ** 的案例,我会这样做:static char kw[][16] = "mode", "name", "ip", "port"; static char * kwlist[] = kw[0], kw[1], kw[2], kw[3], NULL; @elcuco:我不确定 C++ 静态数组是如何工作的。这真的会复制任何数据,而不仅仅是指针吗? 虽然这种方法在某些情况下可能有优点,但盲目应用它是 IMO 可能弊大于利。盲目地应用它很容易导致悬空指针。它还会用无意义的字符串副本使代码膨胀。【参考方案13】:

代替:

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

这行得通:

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

【讨论】:

这是正确的方法,因为您不应该将(常量)字符串传递给需要非常量字符串的函数!【参考方案14】:

为什么不使用-Wno-deprecated 选项来忽略已弃用的警告消息?

【讨论】:

【参考方案15】:
PyTypeObject PyDict_Type=
 ...

PyTypeObject PyDict_Type=

  PyObject_HEAD_INIT(&PyType_Type),
                     "dict",
                     dict_print,
                     0,
                     0
; 

注意 name 字段,在 gcc 中它编译时没有警告,但在 g++ 中它会,我不知道为什么。

gcc (Compiling C)中,-Wno-write-strings 默认是激活的。

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

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

【讨论】:

【参考方案16】:

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

char str[] = "Test string";

或:

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

【讨论】:

【参考方案17】:

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

示例:

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

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

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

const char* s="constant string";  

【讨论】:

【参考方案18】:

以下是如何在文件中内联执行此操作,因此您不必修改 Makefile。

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

您可以稍后...

#pragma GCC diagnostic pop

【讨论】:

【参考方案19】:

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

例如,此代码会生成警告:

putenv("DEBUG=1");

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

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

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

听听编译器告诉你什么!

【讨论】:

而且它还会泄漏为该可写字符串分配的内存。 是的——这是故意的。如上所述,一次性(例如初始化)代码不是问题。或者,您可以自己管理内存并在使用完后释放它。 putenv() 的特殊情况令人担忧——这不是一个很好的示例选择(至少,与此答案相比,putenv() 所做的事情没有更多的讨论)。这是一个完全独立的讨论。 (请注意,基于 POSIX 定义之前的遗留实现,putenv() 行为的 POSIX 规范是有问题的。) IIRC,在最近(本千年)发布的 GNU C 库中存在一个与putenv() 行为改变,并被改回。) 此外,它会带来相对巨大的性能损失。【参考方案20】:

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

【讨论】:

是否可以使用编译指示对每个文件基本禁用它。 @PriyankBolia bdonlan 评论了 Rob Walker 的回答,它可以使用 #pragma GCC diagnostic ignored "-Wwrite-strings" 除非您控制 API,在这种情况下,@John 在下面的回答中关于更改签名以接受 const char* 更正确。 这是非常糟糕的做法,我很遗憾它得到了所有这些选票。不存在警告,因此您可以忽略它们。那里有警告告诉你“伙计,你做的事情可能是错的,小心点”,只有当你想回应“闭嘴,我知道我在做什么”时,你才应该压制它们,这很可能婴儿程序员并非如此。 我同意,您不应该摆脱警告,而是使用 John 提供的解决方案。太糟糕了,这是公认的答案!【参考方案21】:

看到这种情况:

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
;

注意 name 字段,在 gcc 中它编译时没有警告,但在 g++ 中它会,我不知道为什么。

【讨论】:

gcc 暗示将文件视为 C 源文件,g++ 将其视为 c++ 源文件,除非被 -x 覆盖?选项。所以不同的语言,c 和 c++ 对于应该警告的内容有细微的差别。【参考方案22】:

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

char *setf = tigetstr("setf");

到这里:

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

【讨论】:

+1 - 您不能更改应用程序的左值,只能更改右值。事实证明,这解决了真正的问题。其他的只是解决编译器的一些问题。 真正烦人的是 tigetstr() 的原型应该是 (const char *),而不是 (char *) 当我这样做时,我得到“警告:从类型 'const char*' 转换为类型 'char*' 会抛弃 constness”。我不得不使用 const_cast 来消除所有警告: const_cast("setf") 我认为 const cast 是此页面上第一个可接受的解决方案(API 更改除外)。【参考方案23】:

如果它是一个活动的代码库,您可能仍需要升级代码库。当然,手动执行更改是不可行的,但我相信这个问题可以通过一个sed 命令一劳永逸地解决。不过,我还没有尝试过,所以请对以下内容持保留态度。

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

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

【讨论】:

它只解决声明警告而不是函数调用 +1 用于 sed fu 无论如何:p

以上是关于如何摆脱 GCC 中“从字符串常量到‘char*’的不推荐转换”警告?的主要内容,如果未能解决你的问题,请参考以下文章

C ++ - 从字符串常量到'char *'的不推荐转换[重复]

C++ 警告:不推荐使用从字符串常量到“char*”的转换 [-Wwrite-strings]

通过负面警告摆脱 gcc shift

如何为gcc指定默认的全局变量对齐?

Windows 64x下的GCC中的printf和%llx

gcc7 的 NVCC 错误