¿Qué significa este error? No puedo resolverlo de ninguna manera.
advertencia: conversión obsoleta de constante de cadena a 'char *' [-Wwrite-strings]
¿Qué significa este error? No puedo resolverlo de ninguna manera.
advertencia: conversión obsoleta de constante de cadena a 'char *' [-Wwrite-strings]
Respuestas:
Como es mi costumbre, voy a proporcionar un poco de información técnica de fondo sobre por qué y por qué este error.
Voy a inspeccionar cuatro formas diferentes de inicializar cadenas C y veré cuáles son las diferencias entre ellas. Estas son las cuatro formas en cuestión:
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";
Ahora, para esto, voy a querer cambiar la tercera letra "i" por una "o" para que sea "Thos is some text". Eso podría, en todos los casos (se podría pensar), lograrlo:
text[2] = 'o';
Ahora veamos qué hace cada forma de declarar la cadena y cómo esa text[2] = 'o';
declaración afectaría las cosas.
En primer lugar la forma más comúnmente visto: char *text = "This is some text";
. ¿Qué significa esto literalmente? Bueno, en C, literalmente significa "Crear una variable llamada text
que es un puntero de lectura-escritura a este literal de cadena que se mantiene en el espacio de solo lectura (código)". Si tiene la opción -Wwrite-strings
activada, recibirá una advertencia como se ve en la pregunta anterior.
Básicamente eso significa "Advertencia: ha intentado crear una variable que sea punto de lectura-escritura en un área en la que no puede escribir". Si intenta y luego establece el tercer carácter en "o", de hecho estaría intentando escribir en un área de solo lectura y las cosas no serán agradables. En una PC tradicional con Linux que da como resultado:
Fallo de segmentación
Ahora la segunda: char text[] = "This is some text";
. Literalmente, en C, eso significa "Crear una matriz de tipo" char "e inicializarla con los datos" Este es un texto \ 0 ". El tamaño de la matriz será lo suficientemente grande como para almacenar los datos". De modo que en realidad asigna RAM y copia el valor "Esto es texto \ 0" en tiempo de ejecución. Sin advertencias, sin errores, perfectamente válido. Y la forma correcta de hacerlo si desea poder editar los datos . Intentemos ejecutar el comando text[2] = 'o'
:
Es un texto
Funcionó perfectamente. Bueno.
Ahora la tercera vía: const char *text = "This is some text";
. Nuevamente el significado literal: "Crear una variable llamada" texto "que sea un puntero de solo lectura a estos datos en la memoria de solo lectura". Tenga en cuenta que tanto el puntero como los datos ahora son de solo lectura. Sin errores, sin advertencias. ¿Qué sucede si intentamos ejecutar nuestro comando de prueba? Pues no podemos. El compilador ahora es inteligente y sabe que estamos tratando de hacer algo malo:
error: asignación de ubicación de solo lectura '* (texto + 2u)'
Ni siquiera se compilará. Intentar escribir en la memoria de solo lectura ahora está protegido porque le hemos dicho al compilador que nuestro puntero es la memoria de solo lectura. Por supuesto, no tiene que estar apuntando a la memoria de solo lectura, pero si lo señala a la memoria de lectura-escritura (RAM) esa memoria aún estará protegida contra la escritura del compilador.
Finalmente, el último formulario: const char text[] = "This is some text";
. Nuevamente, como antes []
, asigna una matriz en RAM y copia los datos en ella. Sin embargo, ahora se trata de una matriz de solo lectura. No puede escribir porque el puntero está etiquetado como const
. Intentar escribir le da como resultado:
error: asignación de ubicación de solo lectura '* (texto + 2u)'
Entonces, un resumen rápido de dónde estamos:
Este formulario es completamente inválido y debe evitarse a toda costa. Abre la puerta a todo tipo de cosas malas que suceden:
char *text = "This is some text";
Este formulario es el correcto si desea que los datos sean editables:
char text[] = "This is some text";
Este formulario es el correcto si desea cadenas que no se editarán:
const char *text = "This is some text";
Esta forma parece un desperdicio de RAM, pero tiene sus usos. Sin embargo, es mejor olvidarlo por ahora.
const char text[] = "This is some text";
PROGMEM
, PSTR()
o F()
. Por lo tanto, const char text[]
no usa más RAM que const char *text
.
(const char *)(...)
casting. Sin efecto real si la placa no lo necesita, pero un gran ahorro si luego transfiere su código a una placa que sí lo necesita.
Para profundizar en la excelente respuesta de Makenko, hay una buena razón por la cual el compilador le advierte sobre esto. Hagamos un boceto de prueba:
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
Tenemos dos variables aquí, foo y bar. Modifico uno de esos en setup (), pero veo cuál es el resultado:
Thos is some text
Thos is some text
¡ Ambos se cambiaron!
De hecho, si miramos las advertencias, vemos:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
El compilador sabe que esto es dudoso, ¡y es correcto! La razón de esto es que el compilador (razonablemente) espera que las constantes de cadena no cambien (ya que son constantes). Por lo tanto, si se refiere a la cadena constante "This is some text"
varias veces en su código, se le permite asignar la misma memoria a todos ellos. ¡Ahora si modifica uno, los modifica a todos!
*foo
y *bar
usando diferentes "constantes" de cadenas evitaría que esto suceda? Además, ¿cómo es esto diferente de no poner ninguna secuencia en absoluto, como char *foo;
:?
new
, con strcpy
y delete
).
Deje de intentar pasar una constante de cadena donde una función toma un char*
, o cambie la función para que tome un const char*
lugar.
Cadenas como "cadena aleatoria" son constantes.
Ejemplo:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
Advertencia:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
La función foo
espera un char * (que, por lo tanto, puede modificar) pero está pasando un literal de cadena, que no debe modificarse.
El compilador le advierte que no haga esto. Al quedar en desuso, podría convertirse de una advertencia en un error en una futura versión del compilador.
Solución: haga que foo tome un constante char *:
void foo (const char * s)
{
Serial.println (s);
}
No lo entiendo ¿Quieres decir que no puede puede modificar?
Las versiones anteriores de C (y C ++) le permiten escribir código como en mi ejemplo anterior. Podrías hacer una función (comofoo
) que imprima algo que le pasa, y luego pasar una cadena literal (por ejemplo foo ("Hi there!");
) .
Sin embargo, una función que toma char *
como argumento puede modificar su argumento (es decir, modificar Hi there!
en este caso).
Es posible que haya escrito, por ejemplo:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
Desafortunadamente, al pasar un literal, ahora ha modificado potencialmente ese literal para que "¡Hola!" ahora es "Adiós" que no es bueno. De hecho, si copió en una cadena más larga, podría sobrescribir otras variables. O, en algunas implementaciones, obtendría una infracción de acceso porque "¡Hola!" podría haberse colocado en RAM de solo lectura (protegida).
Por lo tanto, los compiladores-escritores están despreciando gradualmente este uso, de modo que las funciones a las que se pasa un literal, deben declarar ese argumento como const
.
can not
ser modificado?
Tengo este error de compilación:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
Por favor reemplace esta línea:
#define TIME_HEADER "T" // Header tag for serial time sync message
con esta linea:
#define TIME_HEADER 'T' // Header tag for serial time sync message
y la compilación va bien.