ChatGPT解决这个技术问题 Extra ChatGPT

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

所以我正在研究一个非常大的代码库,最近升级到 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 的回答 stackoverflow.com/questions/56522654/…
564个文件是完全可行的。去做就对了。 (好吧,你现在很可能已经做到了;-))。

p
phoenix

您向其中传递字符串文字 "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 * 的 3rd 方 API,因此在这种情况下,我通常会进行转换。
@ppumkin 不幸的是,许多 C 标准库字符串函数将参数作为 char* ,即使对于不会被修改的字符串也是如此。如果您将参数作为 char const* 并将其传递给采用 char* 的标准函数,您将成功。如果库函数不会处理字符串,您可以丢弃 const
仅仅因为它并不总是可能的并不意味着它不是很多时候此警告出现在常见生产代码中的首选选项。
我现在完全理解了解决方案,以及字符串文字的功能。但也许其他人没有,所以我“认为”需要解释
我不明白如何应用您的解决方案:(
T
Tim Cooper

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


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

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

#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 可能弊大于利。盲目地应用它很容易导致悬空指针。它还会用无意义的字符串副本使代码膨胀。
C
Community

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

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


实际上:#pragma GCC 诊断忽略了“-Wwrite-strings”
这个答案实际上并不包含答案。
K
Konrad Rudolph

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

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

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


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

这是在文件中内联的方法,因此您不必修改 Makefile。

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

以后可以...

#pragma GCC diagnostic pop

v
vy32

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

char *setf = tigetstr("setf");

对此:

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

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

代替

char *str = "hello";

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

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

foo("hello");

将其替换为

foo((char*) "hello");

g
gipinani

代替:

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

这有效:

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

这是正确的方法,因为您不应该将(常量)字符串传递给需要非常量字符串的函数!
L
LogicStuff

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

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

S
Sicco

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

char str[] = "Test string";

或者:

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

L
LogicStuff

为什么不只使用类型转换?

(char*) "test"

当指针为 const 时,它指向您不应该(或不能)更改的内容。将其转换为非常量允许代码(尝试)更改它。花几天时间找出例如命令停止工作的原因总是很有趣,然后发现某些东西修改了比较中使用的 const 关键字。
@Technophile:在许多情况下,传递给函数的指针将用于生成一个指针,该指针可供调用者或由此控制的代码使用(例如,给回调)。标准库中的一个示例是 strchr()strchr() 函数不会更改由传入指针标识的存储,但它应该没有理由关心调用者是否会使用返回的指针来修改该存储。
@supercat 您是否在反对 const 正确性?是的, strchr() (它违反了 const 正确性:一个 30 多年前的设计错误,已在 C++ 版本中修复)并不“关心”;它是代码,没有情感。关怀是开发人员的工作。将一个指针或从它派生的指针传递给其他代码如何使 const 正确性无效?
@Technophile:如果一个函数有一个接收回调的函数,以及一个应该传递给回调的数据指针,那么如果回调不会修改它,那么将 const 数据传递给函数应该是合法的,或者有回调修改非常量数据。如果调用代码同时提供指向回调及其数据的指针,则调用代码将能够确保它们都同意,但如果调用代码从外部代码接收它们,则无法这样做。因此,const 正确性将需要复制大量代码。
L
LogicStuff

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

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

S
Sohrab

在 C++ 中,替换:

char *str = "hello";

和:

std::string str ("hello");

如果你想比较它:

str.compare("HALLO");

M
MyGEARStationcom

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

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

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

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

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

所有警告都消失了。


这是根据警告所说的正确答案:“警告:不推荐从字符串常量到'char *'的转换”。
s
svick

看到这种情况:

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++ 在应该警告的内容上有细微的差别。
J
Jason Plank

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

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

putenv("DEBUG=1");

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

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

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

听听编译器告诉你什么!


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

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

例子:

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

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

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

const char* s="constant string";  

D
Drew Noakes

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


M
Micheal Morrow

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

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


A
AAEM

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]

R
Ramarajan
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-strings 默认情况下处于活动状态

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


J
James Antill

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

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


人们使用 -Werror 的原因是为了修复警告。否则他们永远不会得到修复。
人们使用 -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/赛通)。然后必须通过质量检查定期审查每个被忽略的警告,以查看它们现在是否可以通过代码避免,或者是否还不可能。