Snprintf : Invalid conversion from 'const char*' to 'char*'

Hi guys,

Why does snprintf gives me an invalid conversion warning for my code.
As you will notice, I'm not using String, because it uses dynamic memory allocation.

const char *Thermometre::GetCSV() {
  memset ((char *)CSV, 0, sizeof(CSV));
  char str_temp[8] = "";
  memset (str_temp, 0, sizeof(str_temp));
  dtostrf(lastReading, 4, 2, str_temp);

  snprintf (CSV, sizeof(CSV), "%s;", str_temp); // TODO Fix invalid conversion

  return CSV;
}

Thank you

Don't you think it would be a good idea to show us how CSV is defined? Post a complete code compiles with the error you're observing.

Sure.

const char Thermometre::CSV[CSV_LENGTH] = "";

const char *Thermometre::GetCSV() {
  memset ((char *)CSV, 0, sizeof(CSV));
  char str_temp[8] = "";
  memset (str_temp, 0, sizeof(str_temp));
  dtostrf(lastReading, 4, 2, str_temp);

  snprintf (CSV, sizeof(CSV), "%s;", str_temp); // TODO Fix invalid conversion

  return CSV;
}

Full warning

src\Thermometre.cpp: In member function 'virtual const char* Thermometre::GetCSV()':
src\Thermometre.cpp:137:46: warning: invalid conversion from 'const char*' to 'char*' [-fpermissive]
   snprintf (CSV, sizeof(CSV), "%s;", str_temp); // TODO Fix invalid conversion
                                              ^
In file included from C:\.platformio\packages\framework-arduino-avr\cores\arduino/Print.h:24:0,
                 from C:\.platformio\packages\framework-arduino-avr\cores\arduino/Stream.h:26,
                 from C:\.platformio\packages\framework-arduino-avr\cores\arduino/HardwareSerial.h:29,
                 from C:\.platformio\packages\framework-arduino-avr\cores\arduino/Arduino.h:233,
                 from src\Thermometre.cpp:1:
c:\.platformio\packages\toolchain-atmelavr\avr\include\stdio.h:687:12: note:   initializing argument 1 of 'int snprintf(char*, size_t, const char*, ...)'
 extern int snprintf(char *__s, size_t __n, const char *__fmt, ...);
            ^~~~~~~~
const char Thermometre::CSV[CSV_LENGTH] = "";

'const' is your promise to the compiler that you won't attempt to change the values in that array. You're breaking that promise with the snprintf() call.

BTW, you're also breaking it with the memset(). But, the cast is keeping the compiler quiet about it.

1 Like

Then what would be the best solution? Because I repeat the const char * in multiple places.

That would depend on why you declared the CSV array to be 'const' in the first place.

C/C++ will convert from char * to const char* for you without error if the method argument requires it.

GetCSV is used for logging purpose. It's called in a loop.

Since char array tends to take a lot of memory, I want to limit the RAM size by using a fixed memory space. It's declared static in the class.

it takes as much memory as you tell it to take. const char * is just a pointer to char array

Does the const char* always keep the same memory address? Is it more efficient to use char*?

Nice use of snprintf. Where you going to check the for failure there or are you just happy to have an empty CSV returned if snprintf fails. No need for the memset CSV, unless you code somewhere else does not just treat it as a null terminated string.

Also while snprintf is 'safe' against buffer overflows, dtostrf is not. The 'width' 4 is the minimum width and does not prevent a large lastReading from overflowing str_temp.
If you really want bullet proof code, without using Strings, you could add

 if (abs(lastReading) > 999) {
    str_temp[0] = '\0';
 } else {
  dtostrf(lastReading, 4, 2, str_temp);
}

again no need for the memset str_temp

As an alternative to Strings take a look at my SafeString library which does not use dynamic memory, just char[ ]s but protects you against buffer overflows, missing '\0' etc and outputs detailed error messages if it finds any, while provide the similar high level text manipulation as Strings. Plus extras
In particular, the SafeString print(lastReading, 2, 8) will format lastReading into the char[ ] held in SafeString and pad to 8 char wide while protecting against buffer overflow. You can also justify left or right and force a + sign if you want using extra args options.

Edit -- SafeString lets you 'wrap' a class static char[ ] so you can update it safely using the SafeString methods.

1 Like

Is a signal to the compiler that the contents of this char* are not expected to be changed by the method and to complain if the code tries to update anything pointed to by that char*.
So const char* and char* are actually the same address, only the compilation checks are changed.

Thanks for your help! I'll surely now take a look at SafeString.

no, it is just a variable, you can point it to a different memory address to different char array. const there means content of the memory should not be changed but not the address pointer is holding. you can make pointer constant too then it will always be pointing to the same address.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.