conversão descontinuada da constante de cadeia para 'char *'


16

O que esse erro significa? Eu não posso resolver isso de forma alguma.

aviso: conversão descontinuada da constante de string para 'char *' [-Wwrite-strings]


Esta questão deve estar em StackOverflow, não Arduino :)
Vijay Chavda

Respostas:


26

Como é meu costume, vou fornecer um pouco de informações técnicas básicas sobre os porquês e os motivos desse erro.

Vou inspecionar quatro maneiras diferentes de inicializar cadeias C e ver quais são as diferenças entre elas. Estas são as quatro maneiras em questão:

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";

Agora, vou querer mudar a terceira letra "i" para "o" para torná-la "Thos is some text". Isso poderia, em todos os casos (você pensaria), ser alcançado por:

text[2] = 'o';

Agora vamos ver o que cada maneira de declarar a string faz e como essa text[2] = 'o';declaração afetaria as coisas.

Primeiro a forma mais comumente visto: char *text = "This is some text";. O que isso significa literalmente? Bem, em C, significa literalmente "Crie uma variável chamada textque é um ponteiro de leitura-gravação para esta cadeia de caracteres literal que é mantida no espaço somente leitura (código)". Se você tiver a opção -Wwrite-stringsativada, receberá um aviso, como visto na pergunta acima.

Basicamente, isso significa "Aviso: você tentou fazer uma variável que seja de leitura / gravação apontar para uma área na qual não é possível gravar". Se você tentar definir o terceiro caractere como "o", na verdade você tentaria gravar em uma área somente leitura e as coisas não serão boas. Em um PC tradicional com Linux, resulta em:

Falha de segmentação

Agora o segundo: char text[] = "This is some text";. Literalmente, em C, isso significa "Crie uma matriz do tipo" char "e inicialize-a com os dados" Isso é algum texto \ 0 ". O tamanho da matriz será grande o suficiente para armazenar os dados". Então, na verdade, ele aloca RAM e copia o valor "Isso é algum texto \ 0" para ele em tempo de execução. Sem avisos, sem erros, perfeitamente válidos. E a maneira certa de fazer isso, se você quiser editar os dados . Vamos tentar executar o comando text[2] = 'o':

Thos é algum texto

Funcionou perfeitamente. Boa.

Agora, a terceira via: const char *text = "This is some text";. Novamente, o significado literal: "Crie uma variável chamada" texto "que é um ponteiro somente leitura para esses dados na memória somente leitura.". Observe que agora o ponteiro e os dados são somente leitura. Sem erros, sem avisos. O que acontece se tentarmos executar nosso comando de teste? Bem, nós não podemos. O compilador agora é inteligente e sabe que estamos tentando fazer algo ruim:

erro: atribuição do local somente leitura '* (texto + 2u)'

Nem compilará. Agora, tentar gravar na memória somente leitura está protegido, porque informamos ao compilador que nosso ponteiro é a memória somente leitura. Obviamente, ele não precisa apontar para a memória somente leitura, mas se você apontar para a memória de leitura e gravação (RAM), a memória ainda estará protegida contra gravação pelo compilador.

Finalmente, a última forma: const char text[] = "This is some text";. Novamente, como antes, []ele aloca uma matriz na RAM e copia os dados para ela. No entanto, agora essa é uma matriz somente leitura. Você não pode gravar porque o ponteiro está marcado como const. Tentar escrever nele resulta em:

erro: atribuição do local somente leitura '* (texto + 2u)'

Portanto, um rápido resumo de onde estamos:

Este formulário é completamente inválido e deve ser evitado a todo custo. Abre a porta para todo tipo de coisa ruim acontecendo:

char *text = "This is some text";

Este formulário é o formulário correto se você deseja tornar os dados editáveis:

char text[] = "This is some text";

Este formulário é o formulário correto se você deseja seqüências de caracteres que não serão editadas:

const char *text = "This is some text";

Este formulário parece um desperdício de RAM, mas tem seus usos. Melhor esquecer por enquanto.

const char text[] = "This is some text";

6
Vale notar que nos Arduinos (pelo menos os baseados no AVR), os literais de string vivem na RAM, a menos que você os declare com uma macro como PROGMEM, PSTR()ou F(). Portanto, const char text[]não usa mais RAM que const char *text.
Edgar Bonet

O Teensyduino e muitos outros compatíveis com arduino mais recentes colocam automaticamente literais de string no espaço de código, portanto vale a pena verificar se F () é ou não necessário em sua placa.
Craig.Feied

@ Craig.Feied Em geral, F () deve ser usado independentemente. Aqueles que não "precisam" tendem a defini-lo como um (const char *)(...)elenco simples . Não há efeito real se a placa não precisar, mas uma grande economia se você portar seu código para uma placa que precise.
Majenko

5

Para elaborar a excelente resposta de Makenko, há uma boa razão pela qual o compilador avisa sobre isso. Vamos fazer um esboço de teste:

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

Temos duas variáveis ​​aqui, foo e bar. Modifico um dos itens em setup (), mas veja qual é o resultado:

Thos is some text
Thos is some text

Eles tanto tem mudado!

De fato, se olharmos para os avisos, 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*’

O compilador sabe que isso é desonesto, e está certo! A razão para isso é que o compilador (razoavelmente) espera que as constantes da string não sejam alteradas (uma vez que são constantes). Portanto, se você se referir à constante da cadeia de caracteres "This is some text"várias vezes no seu código, é permitido alocar a mesma memória para todos eles. Agora, se você modificar um, você modifica todos eles!


Sagrada fumaça! Quem saberia ... Isso ainda é verdade para os compiladores mais recentes do ArduinoIDE? Eu apenas tentei em um ESP32 e causa repetidos erros de GuruMeditação .
Not2qubit 04/07/19

@ not2qubit Acabei de testar no Arduino 1.8.9 e é verdade lá.
Nick Gammon

Os avisos estão lá por uma razão. Dessa vez, recebi: warning: O ISO C ++ proíbe a conversão de uma constante de string em 'char ' [-Wwrite-strings] char bar = "Este é um texto"; - PROIBIR é uma palavra forte. Como você está proibido de fazer isso, o compilador é livre para mexer e compartilhar a mesma string em duas variáveis. Não faça coisas proibidas ! (Leia também e elimine os avisos). :)
Nick Gammon

Portanto, caso você encontre um código de baixa qualidade como esse e queira sobreviver ao dia. Uma declaração inicial *fooe o *baruso de diferentes "constantes" de string impediriam que isso acontecesse? Além disso, como isso é diferente de não colocar nenhuma string, como char *foo;:?
Not2qubit

1
Constantes diferentes podem ajudar, mas, pessoalmente, eu não colocaria nada lá e colocaria os dados da maneira usual mais tarde (por exemplo new, com strcpye delete).
Nick Gammon

4

Pare de tentar passar uma constante de cadeia de caracteres em que uma função recebe um char*ou altere a função para que ele aceite um const char*.

Sequências como "sequência aleatória" são constantes.


Um texto como "caracteres aleatórios" é um caractere constante?
Federico Corazza

1
Literais de string são constantes de string.
Ignacio Vazquez-Abrams

3

Exemplo:

void foo (char * s)
  {
  Serial.println (s);
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo ("bar");
  }  // end of setup

void loop ()
  {
  }  // end of loop

Atenção:

sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’

A função fooespera um caractere * (que, portanto, pode ser modificado), mas você está passando uma string literal, que não deve ser modificada.

O compilador está avisando para não fazer isso. Sendo preterido, pode passar de um aviso para um erro em uma versão futura do compilador.


Solução: Faça foo tomar um const char *:

void foo (const char * s)
  {
  Serial.println (s);
  }

Eu não entendo. Você quer dizer que não pode ser modificado?

As versões mais antigas do C (e C ++) permitem escrever código como no meu exemplo acima. Você pode criar uma função (como foo) que imprime algo que você transmite a ela e depois transmitir uma string literal (por exemplo foo ("Hi there!");) .

No entanto, uma função que assume char *como argumento pode modificar seu argumento ( Hi there!por exemplo, modificar neste caso).

Você pode ter escrito, por exemplo:

void foo (char * s)
  {
  Serial.println (s);
  strcpy (s, "Goodbye");
  }

Infelizmente, passando um literal, você agora modificou esse literal para que "Olá!" agora é "adeus", o que não é bom. De fato, se você copiou uma string mais longa, pode sobrescrever outras variáveis. Ou, em algumas implementações, você receberá uma violação de acesso porque "Olá!" pode ter sido colocado na RAM somente leitura (protegida).

Portanto, os compiladores-escritores estão gradualmente descontinuando esse uso, de modo que as funções às quais você passa um literal devem declarar esse argumento como const.


É um problema se eu não usar um ponteiro?
Federico Corazza

Que tipo de problema? Esse aviso específico é sobre a conversão de uma constante de seqüência de caracteres em um ponteiro char *. Você pode elaborar?
Nick Gammon

@ Nick: O que você quer dizer com "(..) você está passando uma string literal, que não deve ser modificada". Eu não entendo. Você quer dizer can notser modificado?
Mads Skjern

Eu modifiquei minha resposta. Majenko abordou a maioria desses pontos em sua resposta.
Nick Gammon

1

Eu tenho este erro de compilação:

TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
   if(Serial.find(TIME_HEADER)) {

                         ^

Substitua esta linha:
#define TIME_HEADER "T" // Header tag for serial time sync message

com esta linha:
#define TIME_HEADER 'T' // Header tag for serial time sync message

e compilação vai bem.


3
Essa mudança altera a definição de uma cadeia de um caractere "T" para um único caractere com o valor do código ASCII para o T. letra maiúscula
DLU
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.