What does this error means? I can't solve it in any way.
warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
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 text\0". 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 text\0" 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";
PROGMEM
, PSTR()
or F()
. Thus, const char text[]
does not use more RAM than const char *text
.
Commented
Jul 16, 2015 at 7:35
(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.
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!
*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;
?
Commented
Jul 5, 2019 at 8:41
new
, strcpy
and delete
).
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.
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
.
can not
be modified?
Commented
Mar 1, 2016 at 14:44
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.