此错误是什么意思?我无法以任何方式解决。
警告:不建议将字符串常量转换为'char *'[-Wwrite-strings]
此错误是什么意思?我无法以任何方式解决。
警告:不建议将字符串常量转换为'char *'[-Wwrite-strings]
Answers:
尽我所能,我将提供一些背景技术信息来说明此错误的原因和原因。
我将检查初始化C字符串的四种不同方法,并查看它们之间的区别。这些是有问题的四种方法:
char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";
现在,我要将第三个字母“ i”更改为“ o”,使其变为“ Thos is some text”。在所有情况下(您会认为),都可以通过以下方式实现:
text[2] = 'o';
现在,让我们看一下声明字符串的每种方式以及该text[2] = 'o';
声明将如何影响事物。
首先是最常见的方式:char *text = "This is some text";
。这从字面上是什么意思?好吧,在C语言中,它的字面意思是“创建一个名为的变量text
,该变量是对此字符串文字的读写指针,该字符串文字存储在只读(代码)空间中”。如果您-Wwrite-strings
打开了该选项,则会收到警告,如上面的问题所示。
基本上,这意味着“警告:您已尝试将一个读写变量指向无法写入的区域”。如果您尝试将第三个字符设置为“ o”,则实际上您将尝试写入一个只读区域,并且情况会变糟。在装有Linux的传统PC上,结果为:
分段故障
现在第二个:char text[] = "This is some text";
。从字面上看,在C语言中,这意味着“创建一个类型为“ char”的数组,并使用数据“ This is some text \ 0”对其进行初始化。该数组的大小将足以存储数据。这样实际上就分配了RAM,并在运行时将值“ This is some text \ 0”复制到其中。没有警告,没有错误,完全有效。正确的方法是编辑数据。让我们尝试运行命令text[2] = 'o'
:
这是一些文字
效果很好。好。
现在的第三条道路:const char *text = "This is some text";
。再次字面意思:“创建一个名为变量‘文本’这是一个只读指向这个数据在只读存储器中。” 注意,指针和数据现在都是只读的。没有错误,没有警告。如果尝试运行测试命令会怎样?好吧,我们不能。编译器现在很聪明,并且知道我们正在尝试做一些不好的事情:
错误:分配了只读位置'*(text + 2u)'
它甚至不会编译。尝试写入只读存储器现在受到保护,因为我们已经告诉编译器我们的指针是只读存储器。当然,它不会有被指向只读存储器,但如果你点它来读写存储器(RAM)内存将仍然由编译器被写入保护。
最后是最后一种形式:const char text[] = "This is some text";
。再次,像以前一样,[]
它在RAM中分配一个数组并将数据复制到其中。但是,现在这是一个只读数组。您无法写入它,因为指向它的指针标记为const
。尝试写入它会导致:
错误:分配了只读位置'*(text + 2u)'
因此,我们简要概述一下:
此表格完全无效,应不惜一切代价避免使用。它为各种不良事件打开了大门:
char *text = "This is some text";
如果您想使数据可编辑,则此表格是正确的表格:
char text[] = "This is some text";
如果您希望不编辑字符串,则此表单是正确的表单:
const char *text = "This is some text";
这种形式似乎浪费了RAM,但确实有其用途。最好暂时忘记它。
const char text[] = "This is some text";
PROGMEM
,PSTR()
或宏声明它们F()
。因此,const char text[]
使用的RAM不超过const char *text
。
(const char *)(...)
转换。如果板子不需要它,则没有真正的效果,但是如果您将代码移植到需要的板上,则可以节省很多。
为了详细说明Makenko的出色答案,编译器会为此警告您有充分的理由。让我们做一个测试草图:
char *foo = "This is some text";
char *bar = "This is some text";
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo [2] = 'o'; // change foo only
Serial.println (foo);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
这里有两个变量,foo和bar。我在setup()中修改了其中之一,但是看到的结果是:
Thos is some text
Thos is some text
他们都变了!
实际上,如果我们查看警告,则会看到:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
编译器知道这是狡猾的,这是正确的!这样做的原因是,编译器(合理地)期望字符串常量不变(因为它们是常量)。因此,如果您"This is some text"
在代码中多次引用字符串常量,则可以为所有字符串分配相同的内存。现在,如果您修改其中的一个,就可以全部修改!
*foo
并*bar
使用不同的字符串“ constants”进行初始声明是否可以防止这种情况发生?此外,如何从不把任何条件可言,这样的不同:char *foo;
?
new
,使用strcpy
和和delete
)。
要么停止尝试在函数采用的地方传递字符串常量char*
,要么更改函数以使其const char*
取而代之。
诸如“随机字符串”之类的字符串是常量。
例:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
警告:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
该函数foo
需要一个char *(因此可以修改),但是您传递的是字符串文字,不应对其进行修改。
编译器警告您不要这样做。不建议使用它,它可能在将来的编译器版本中从警告变为错误。
解决方案:使foo接受const char *:
void foo (const char * s)
{
Serial.println (s);
}
我不明白 你是说不能修改?
较旧的C(和C ++)版本使您可以像上面的示例一样编写代码。您可以创建一个函数(如foo
),该函数先打印传递给它的内容,然后传递文字字符串(例如foo ("Hi there!");
)
但是,char *
允许将以参数为参数的函数修改其参数(即,Hi there!
在这种情况下进行修改)。
您可能已经写过,例如:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
不幸的是,通过传递文字,您现在可能已经修改了该文字,以便“嗨!” 现在是“再见”,这不好。实际上,如果您复制了更长的字符串,则可能会覆盖其他变量。或者,在某些实现中,您会遇到访问冲突,因为“嗨!” 可能已放入只读(受保护的)RAM中。
因此,编译器-编写器正在逐渐弃用此用法,以便您传递文字的函数必须将该参数声明为const
。
can not
被修改?
我有这个编译错误:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
请替换此行:
#define TIME_HEADER "T" // Header tag for serial time sync message
用这一行:
#define TIME_HEADER 'T' // Header tag for serial time sync message
编译顺利。