Inicializar um char [] com uma string literal é uma prática ruim?

Eu estava lendo um tópico intitulado “strlen vs sizeof” no CodeGuru e uma das respostas afirma que “é” de qualquer maneira [sic] uma má prática inicializar [sic] uma matriz char com uma string literal. “

Isso é verdade ou é apenas a opinião dele (embora seja um” membro de elite “)?


Aqui está a pergunta original:

#include <stdio.h> #include<string.h> main() { char string[] = "october"; strcpy(string, "september"); printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string)); return 0; } 

certo. o tamanho deve ser o comprimento mais 1 sim?

este é a saída

the size of september is 8 and the length is 9

o tamanho deve ser 10 certamente. é como calcular o tamanho da string antes de ser alterado por strcpy, mas o comprimento depois.

Há algo errado com minha sintaxe ou o quê?


Aqui está a resposta :

De qualquer forma, é uma má prática inicializar um array de char com uma string literal. Portanto, sempre faça um dos seguintes:

const char string1[] = "october"; char string2[20]; strcpy(string2, "september"); 

Comentários

  • Observe o ” const ” na primeira linha. Será que o autor assumiu c ++ em vez de c? Em c ++, é ” má prática “, porque um literal deve ser const e qualquer compilador c ++ recente fornecerá um aviso (ou erro) sobre como atribuir um literal const a um array não const.
  • @Andr é C ++ define literais de string como arrays const, porque essa é a única maneira segura de lidar com eles. Que C não ‘ t é o problema, então você tem uma regra social que impõe a coisa segura
  • @Caleth. Eu sei, eu estava mais tentando argumentar que o autor da resposta estava se aproximando da ” má prática ” de uma perspectiva c ++.
  • @Andr é não é ‘ uma má prática em C ++, porque não é ‘ ta praticar , é ‘ um erro de tipo direto. Deve ser um erro de tipo em C, mas não é ‘ t, então você deve ter uma regra de guia de estilo informando ” É ‘ proibido ”

Resposta

De qualquer forma, é uma má prática inicializar uma matriz de caracteres com um literal de string.

O autor desse comentário nunca o justifica, e acho a afirmação intrigante.

Em C (e você “marcou isso como C), isso” É praticamente a única maneira de inicializar uma matriz de char com um valor de string (a inicialização é diferente da atribuição). Você pode escrever

char string[] = "october"; 

ou

char string[8] = "october"; 

ou

char string[MAX_MONTH_LENGTH] = "october"; 

No primeiro caso, o tamanho da matriz é obtido do tamanho do inicializador. Literais de string são armazenados como matrizes de char com um byte de terminação 0, então o tamanho da matriz é 8 (“o”, “c”, “t”, “o”, “b”, “e”, “r”, 0). Nos segundos dois casos, o tamanho da matriz é especificado como parte da declaração (8 e MAX_MONTH_LENGTH, seja o que for).

O que você não pode fazer é escrever algo como

char string[]; string = "october"; 

ou

char string[8]; string = "october"; 

etc. No primeiro caso, a declaração de string é incompleta porque nenhum tamanho de array foi especificado e não há inicializador para obter o tamanho. Em ambos casos, o = não funcionará porque a) uma expressão de matriz como string pode não ser o alvo de uma atribuição eb) o operador = não está definido para copiar o conteúdo de uma matriz para outra.

Com esse mesmo token, você não pode “escrever

char string[] = foo; 

onde foo é outra matriz de char. Esta forma de inicialização só funcionará com literais de string.

EDITAR

Devo corrigir isso para dizer que você também pode inicializar arrays para conter uma string com um inicializador de estilo de matriz, como

char string[] = {"o", "c", "t", "o", "b", "e", "r", 0}; 

ou

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII 

mas é mais fácil para os olhos usar literais de string.

EDITAR 2

Para atribuir o conteúdo de uma matriz fora de uma declaração, você precisaria usar strcpy/strncpy (para strings terminadas em 0) ou memcpy (para qualquer outro tipo de matriz):

if (sizeof string > strlen("october")) strcpy(string, "october"); 

ou

strncpy(string, "october", sizeof string); // only copies as many characters as will // fit in the target buffer; 0 terminator // may not be copied, but the buffer is // uselessly completely zeroed if the // string is shorter! 

Comentários

  • strncpy raramente é a resposta certa
  • @KeithThompson: não discordando, apenas adicionado para integridade ‘ bem.
  • Observe que char[8] str = "october"; é uma má prática. Eu tive que literalmente contar a mim mesmo para ter certeza de que não era ‘ um estouro e quebre durante a manutenção … por exemplo, corrigir um erro ortográfico de seprate para separate será interrompido se o tamanho não for atualizado.
  • Concordo com djechlin, é uma má prática pelas razões apresentadas. A resposta de JohnBode ‘ não ‘ t comenta sobre a ” má prática ” aspecto (que é a parte principal da questão !!), ele apenas explica o que você pode ou não fazer para inicializar a matriz.
  • Menor: As ‘ length ” valor retornado de strlen() não inclui o caractere nulo, usando MAX_MONTH_LENGTH para manter o tamanho máximo necessário para char string[] frequentemente parece errado. IMO, MAX_MONTH_SIZE seria melhor aqui.

Resposta

O único problema de que me lembro é atribuir literal de string a char *:

char var1[] = "september"; var1[0] = "S"; // Ok - 10 element char array allocated on stack char const *var2 = "september"; var2[0] = "S"; // Compile time error - pointer to constant string char *var3 = "september"; var3[0] = "S"; // Modifying some memory - which may result in modifying... something or crash 

Por exemplo, pegue este programa:

#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); } 

Isso em minha plataforma (Linux) trava ao tentar gravar na página marcada como somente leitura. Em outras plataformas, pode imprimir “setembro” etc.

Dito isso – a inicialização por literal cria a quantidade específica de reserva, portanto, não funcionará:

char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep" 

Mas isso vai

char buf[32] = "May"; strncpy(buf, "September", sizeof(buf)); 

Como última observação – eu não usaria strcpy em tudo:

char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory 

Embora alguns compiladores possam alterá-lo para uma chamada segura, strncpy é muito mais seguro:

char buf[1024]; strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers. buf[sizeof(buf) - 1] = "\0"; 

Comentários

  • Há ‘ ainda risco de saturação do buffer naquele strncpy porque não ‘ termina a string copiada quando o comprimento de something_else é maior que sizeof(buf). Normalmente defino o último caractere buf[sizeof(buf)-1] = 0 para proteger contra isso, ou se buf for inicializado com zero, use sizeof(buf) - 1 como o comprimento da cópia.
  • Use strlcpy ou strcpy_s ou mesmo snprintf se for necessário.
  • Corrigido. Infelizmente, não existe uma maneira fácil de fazer isso, a menos que você se dê ao luxo de trabalhar com os compiladores mais novos (strlcpy e snprintf não são diretamente acessíveis no MSVC, pelo menos os pedidos e strcpy_s não estão no * nix).
  • @MaciejPiechotka: Bem, graças a Deus, o Unix rejeitou o anexo k patrocinado pela microsoft.

Resposta

Principalmente porque você não terá o tamanho de char[] em uma variável / construção que você pode usar facilmente dentro do programa.

O exemplo de código do link:

 char string[] = "october"; strcpy(string, "september"); 

string é alocado na pilha com 7 ou 8 caracteres. Não consigo me lembrar se é terminado em nulo desta forma ou não – o tópico vinculado afirmou que é .

Copiar “setembro” sobre essa string é uma saturação de memória óbvia.

Outro desafio surge se você passar string para outra funçãopara que a outra função possa escrever no array. Você precisa dizer à outra função quanto tempo a matriz é, para que ela não crie uma saturação. Você poderia passar string junto com o resultado de strlen() mas o tópico explica como isso pode explodir se string não tiver terminação nula.

Você está melhor alocar uma string com um tamanho fixo (de preferência definido como uma constante) e depois passar a matriz e o tamanho fixo para a outra função. Os comentários de @John Bode “estão corretos e há maneiras de mitigar esses riscos. Eles também exigem mais esforço de sua parte para usá-los.

Na minha experiência, o valor inicializei char[] to geralmente é muito pequeno para os outros valores que preciso colocar lá. Usar uma constante definida ajuda a evitar esse problema.


sizeof string fornecerá o tamanho do buffer (8 bytes); use o resultado dessa expressão em vez de strlen quando estiver preocupado com a memória.
Da mesma forma, você pode fazer uma verificação antes da chamada para strcpy para ver se seu buffer de destino é grande o suficiente para a string de origem: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Sim, se você tiver que passar a matriz para uma função, você “vai também precisa passar seu tamanho físico: foo (array, sizeof array / sizeof *array);. – John Bode

Comentários

  • sizeof string fornecerá o tamanho do buffer (8 bytes); use o resultado dessa expressão em vez de strlen quando você ‘ estiver preocupado com a memória. Da mesma forma, você pode fazer uma verificação antes da chamada para strcpy para ver se seu buffer de destino é grande o suficiente para a string de origem: if (sizeof target > strlen(src)) { strcpy (target, src); }. Sim, se você tiver que passar a matriz para uma função, ‘ precisará passar seu tamanho físico também: foo (array, sizeof array / sizeof *array);.
  • @JohnBode – obrigado, e esses são bons pontos. Eu incorporei seu comentário em minha resposta.
  • Mais precisamente, a maioria das referências ao nome da matriz string resulta em uma conversão implícita para char*, apontando para o primeiro elemento da matriz. Isso perde as informações de limites da matriz. Uma chamada de função é apenas um dos muitos contextos em que isso acontece. char *ptr = string; é outro. Mesmo string[0] é um exemplo disso; o operador [] funciona em ponteiros, não diretamente em matrizes. Leitura sugerida: Seção 6 das perguntas frequentes do comp.lang.c .
  • Finalmente uma resposta que realmente se refere à pergunta!

Resposta

Uma coisa que nenhum dos tópicos traz é esta:

char whopping_great[8192] = "foo"; 

vs.

char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo")); 

O primeiro fará algo como:

memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo")); 

O último faz apenas o memcpy. O padrão C insiste que, se qualquer parte de um array for inicializada, tudo será. Portanto, neste caso, é melhor fazer você mesmo. Acho que pode ter sido isso que treuss queria chegar.

Com certeza

char whopping_big[8192]; whopping_big[0] = 0; 

é melhor do que:

char whopping_big[8192] = {0}; 

ou

char whopping_big[8192] = ""; 

ps For pontos de bônus, você pode fazer:

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo")); 

para lançar uma divisão do tempo de compilação por erro zero se você estiver prestes a estourar o array.

Resposta

Acho que a ideia de “prática ruim” vem do fato de que este formulário:

char string[] = "october is a nice month"; 

cria implicitamente um strcpy do código fonte da máquina para a pilha.

É mais eficiente manipular apenas um link para aquela string. Como com:

char *string = "october is a nice month"; 

ou diretamente:

strcpy(output, "october is a nice month"); 

(mas é claro na maioria código provavelmente não importa)

Comentários

  • Não ‘ faria apenas uma cópia se você tentar modificá-lo? Eu acho que o compilador seria mais inteligente do que isso
  • E casos como char time_buf[] = "00:00"; onde você ‘ você vai modificar um buffer? Um char * inicializado em uma string literal é definido como o endereço do primeiro byte, então tentar modificá-lo resulta em um comportamento indefinido porque o método do armazenamento literal da string ‘ s é desconhecido (implementação definida), enquanto a modificação dos bytes de um char[] é perfeitamente legal porque o a inicialização copia os bytes para um espaço gravável alocado na pilha. Para dizer que ‘ s ” menos eficiente ou ” má prática ” sem entrar em detalhes sobre as nuances de char* vs char[] é enganoso.

Resposta

Nunca é muito tempo, mas você deve evitar a inicialização char [] para string, porque “string” é const char *, e você está atribuindo a char *. Portanto, se você passar este char [] para o método que altera os dados, você pode ter um comportamento interessante.

Como recomendou, misturei um pouco de char [] com char *, isso não é bom, pois eles diferem um pouco.

Não há nada de errado em atribuir dados ao array char, mas como a intenção de usar esse array é usá-lo como “string” (char *), é fácil esquecer que você não deve modificar isso array.

Comentários

  • Incorreto. A inicialização copia o conteúdo do literal de string para o array. O objeto de array isn ‘ t const a menos que você defina dessa forma.(E os literais de string em C não são const, embora qualquer tentativa de modificar um literal de string tenha comportamento indefinido.) char *s = "literal"; tem o tipo de comportamento que você ‘ está falando; ‘ é melhor escrito como const char *s = "literal";
  • ” E geralmente ” asdf ” é uma constante, então deve ser declarada como const. ” – O mesmo raciocínio exigiria um const em int n = 42;, porque 42 é uma constante.
  • Não ‘ importa qual máquina você ‘ está ligada. O padrão de linguagem garante que c seja modificável. É ‘ uma garantia exatamente tão forte quanto a que 1 + 1 avalia como 2. Se o programa ao qual vinculei acima fizer algo diferente de imprimir EFGH, isso indica uma implementação C não conforme.
  • @Dainus: o compilador MSVC tem uma otimização chamada ‘ string pooling ‘ que colocará uma única cópia de strings idênticas em um segmento somente leitura se puder garantir que seus usos sejam somente leitura. Desative a otimização para ver o comportamento ‘ normal ‘. Para sua informação, o ” Editar e continuar ” requer que esta opção esteja ativada. Mais informações aqui: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
  • Acho que Dainius está sugerindo que em muitos casos, o erro é que a própria variável deve ser marcada const char *const para evitar a modificação dos bytes ou do próprio ponteiro, mas em muitos casos os programadores deixarão um ou ambos mutáveis, permitindo que algum código de tempo de execução modifique o que parece ser uma constante digitada (mas não é constante).

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *