19

What does this error means? I can't solve it in any way.

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

1
  • 1
    This question should be on StackOverflow, not Arduino :) Commented Jan 4, 2019 at 6:41

5 Answers 5

30

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";
3
  • 6
    It 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() or F(). Thus, const char text[] does not use more RAM than const char *text. Commented 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. Commented 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
    Commented Jun 12, 2018 at 13:32
6

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!

5
  • 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
    Commented Jul 4, 2019 at 16:44
  • @not2qubit I just tested on Arduino 1.8.9 and it is true there.
    – Nick Gammon
    Commented 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). :)
    – Nick Gammon
    Commented 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
    Commented Jul 5, 2019 at 8:41
  • 1
    Different constants might help, but personally I wouldn't put anything there, and put data there in the usual way later (eg. with new, strcpy and delete).
    – Nick Gammon
    Commented Jul 5, 2019 at 21:49
5

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.

2
  • Is a text like "random characters" a constant char?
    – Facorazza
    Commented Jul 14, 2015 at 2:25
  • 1
    String literals are string constants. Commented Jul 14, 2015 at 2:31
4

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.

4
  • Is it a problem if I don't use a pointer?
    – Facorazza
    Commented 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?
    – Nick Gammon
    Commented 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? Commented Mar 1, 2016 at 14:44
  • I modified my answer. Majenko covered most of these points in his answer.
    – Nick Gammon
    Commented Mar 1, 2016 at 20:42
2

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.

1
  • 3
    This 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
    Commented Jan 3, 2016 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.