What does this error means? I can't solve it in any way.
warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
-
1This question should be on StackOverflow, not Arduino :)Vijay Chavda– Vijay Chavda2019年01月04日 06:41:18 +00:00Commented Jan 4, 2019 at 6:41
5 Answers 5
As is my wont, I'm going to provide a bit of background technical information into the whys and wherefores of this this error.
I'm going to inspect four different ways of initializing C strings and see what the differences between them are. These are the four ways in question:
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";
Now for this I am going to want to change the third letter "i" into an "o" to make it "Thos is some text". That could, in all cases (you would think), be achieved by:
text[2] = 'o';
Now let's look at what each way of declaring the string does and how that text[2] = 'o';
statement would affect things.
First the most commonly seen way: char *text = "This is some text";
. What does this literally mean? Well, in C, it literally means "Create a variable called text
which is a read-write pointer to this string literal which is held in read-only (code) space.". If you have the option -Wwrite-strings
turned on then you get a warning as seen in the question above.
Basically that means "Warning: You have tried to make a variable that is read-write point to an area you can't write to". If you try and then set the third character to "o" you would in fact be trying to write to a read-only area and things won't be nice. On a traditional PC with Linux that results in:
Segmentation Fault
Now the second one: char text[] = "This is some text";
. Literally, in C, that means "Create an array of type "char" and initialize it with the data "This is some text0円". The size of the array will be big enough to store the data". So that actually allocates RAM and copies the value "This is some text0円" into it at runtime. No warnings, no errors, perfectly valid. And the right way to do it if you want to be able to edit the data. Let's try running the command text[2] = 'o'
:
Thos is some text
It worked, perfectly. Good.
Now the third way: const char *text = "This is some text";
. Again the literal meaning: "Create a variable called "text" that is a read only pointer to this data in read only memory.". Note that both the pointer and the data are now read-only. No errors, no warnings. What happens if we try and run our test command? Well, we can't. The compiler is now intelligent and knows that we are trying to do something bad:
error: assignment of read-only location ‘*(text + 2u)’
It won't even compile. Trying to write to read-only memory is now protected because we have told the compiler that our pointer is to read-only memory. Of course, it doesn't have to be pointing to read-only memory, but if you point it to read-write memory (RAM) that memory will still be protected from being written to by the compiler.
Finally the last form: const char text[] = "This is some text";
. Again, like before with []
it allocates an array in RAM and copies the data into it. However, now this is a read-only array. You can't write to it because the pointer to it is tagged as const
. Attempting to write to it results in:
error: assignment of read-only location ‘*(text + 2u)’
So, a quick summary of where we are:
This form is completely invalid and should be avoided at all costs. It opens the door to all sorts of bad things happening:
char *text = "This is some text";
This form is the right form if you are wanting to make the data editable:
char text[] = "This is some text";
This form is the right form if you want strings that won't be edited:
const char *text = "This is some text";
This form seems wasteful of RAM but it does have its uses. Best forget it for now though.
const char text[] = "This is some text";
-
6It is worth noting that on the Arduinos (the AVR-based ones at least), string literals live in RAM, unless you declare them with a macro like
PROGMEM
,PSTR()
orF()
. Thus,const char text[]
does not use more RAM thanconst char *text
.Edgar Bonet– Edgar Bonet2015年07月16日 07:35:52 +00:00Commented Jul 16, 2015 at 7:35 -
Teensyduino and many other more recent arduino-compatibles automatically place string literals in code space, so it's worth checking to see whether or not F() is needed on your board.Craig.Feied– Craig.Feied2018年06月12日 13:30:50 +00:00Commented Jun 12, 2018 at 13:30
-
@Craig.Feied In general F() should be used regardless. Those that don't "need" it tend to define it as a simple
(const char *)(...)
casting. No real effect if the board doesn't need it, but a big saving if you then port your code to a board that does.Majenko– Majenko2018年06月12日 13:32:15 +00:00Commented Jun 12, 2018 at 13:32
To elaborate on Makenko's excellent answer, there is a good reason why the compiler warns you about this. Let's make a test sketch:
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
We have two variables here, foo and bar. I modify one of those in setup(), but see what the result is:
Thos is some text
Thos is some text
They both got changed!
In fact if we look at the warnings we see:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
The compiler knows this is dodgy, and it is right! The reason for this is, that the compiler (reasonably) expects that string constants don't change (since they are constants). Thus if you refer to the string constant "This is some text"
multiple times in your code it is allowed to allocate the same memory to all of them. Now if you modify one, you modify all of them!
-
Holy Smoke! Who would have known... Is this still true for latest ArduinoIDE compilers? I just tried it on an ESP32 and it causes repeated GuruMeditation errors.not2qubit– not2qubit2019年07月04日 16:44:34 +00:00Commented Jul 4, 2019 at 16:44
-
@not2qubit I just tested on Arduino 1.8.9 and it is true there.2019年07月04日 21:38:11 +00:00Commented Jul 4, 2019 at 21:38
-
The warnings are there for a reason. This time I got: warning: ISO C++ forbids converting a string constant to 'char' [-Wwrite-strings] char bar = "This is some text"; - FORBIDS is a strong word. Since you are forbidden to do that, the compiler is free to muck around and share the same string over two variables. Don't do forbidden things! (Also, read and eliminate the warnings). :)2019年07月04日 21:39:58 +00:00Commented Jul 4, 2019 at 21:39
-
So in case you come across crappy code like this, and want to survive the day. Would an initial declaration of
*foo
and*bar
using different string "constants", prevent this from happening? Also, how is this different from not putting any strings at all, like:char *foo;
?not2qubit– not2qubit2019年07月05日 08:41:00 +00:00Commented Jul 5, 2019 at 8:41 -
1Different constants might help, but personally I wouldn't put anything there, and put data there in the usual way later (eg. with
new
,strcpy
anddelete
).2019年07月05日 21:49:05 +00:00Commented Jul 5, 2019 at 21:49
Either stop trying to pass a string constant where a function takes a char*
, or change the function so that it takes a const char*
instead.
String like "random string" are constants.
-
Is a text like "random characters" a constant char?Facorazza– Facorazza2015年07月14日 02:25:09 +00:00Commented Jul 14, 2015 at 2:25
-
1String literals are string constants.Ignacio Vazquez-Abrams– Ignacio Vazquez-Abrams2015年07月14日 02:31:40 +00:00Commented Jul 14, 2015 at 2:31
Example:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
Warning:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
The function foo
expects a char* (which it can therefore modify) but you are passing a string literal, which should not be modified.
The compiler is warning you not to do this. Being deprecated it might turn from a warning into an error in a future compiler version.
Solution: Make foo take a const char *:
void foo (const char * s)
{
Serial.println (s);
}
I don't get it. Do you mean can not be modified?
Older versions of C (and C++) let you write code like my example above. You could make a function (like foo
) that prints something you pass down to it, and then pass down a literal string (eg. foo ("Hi there!");
)
However a function that takes char *
as an argument is allowed to modify its argument (ie. modify Hi there!
in this case).
You might have written, for example:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
Unfortunately, by passing down a literal, you have now potentially modified that literal so that "Hi there!" is now "Goodbye" which is not good. In fact if you copied in a longer string, you might overwrite other variables. Or, on some implementations you would get an access violation because "Hi there!" might have been put in read-only (protected) RAM.
So the compiler-writers are gradually deprecating this usage, so that functions to which you pass down a literal, must declare that argument as const
.
-
Is it a problem if I don't use a pointer?Facorazza– Facorazza2015年07月14日 05:51:49 +00:00Commented Jul 14, 2015 at 5:51
-
What sort of problem? That particular warning is about converting a string constant into a char* pointer. Can you elaborate?2015年07月14日 06:13:10 +00:00Commented Jul 14, 2015 at 6:13
-
@Nick: What do you mean "(..)you are passing a string literal, which should not be modified". I don't get it. Do you mean
can not
be modified?Mads Skjern– Mads Skjern2016年03月01日 14:44:45 +00:00Commented Mar 1, 2016 at 14:44 -
I modified my answer. Majenko covered most of these points in his answer.2016年03月01日 20:42:49 +00:00Commented Mar 1, 2016 at 20:42
I have this compilation error:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
Please replace this line:
#define TIME_HEADER "T" // Header tag for serial time sync message
with this line:
#define TIME_HEADER 'T' // Header tag for serial time sync message
and compilation goes well.
-
3This change changes the define from a one character string "T" to a single character with the value of the ASCII code for the letter capital T.dlu– dlu2016年01月03日 16:07:29 +00:00Commented Jan 3, 2016 at 16:07