Eu estava lendo um tópico intitulado "strlen vs sizeof" no CodeGuru , e uma das respostas afirma que "de qualquer maneira [sic] é uma prática recomendada inicializar [sic] uma char
matriz 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;
}
direito. o tamanho deve ser o comprimento mais 1 sim?
esta é a saída
the size of september is 8 and the length is 9
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 uma matriz de caracteres com uma string literal. Portanto, sempre siga um destes procedimentos:
const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
fonte
Respostas:
O autor desse comentário nunca realmente o justifica, e acho a afirmação intrigante.
Em C (e você marcou isso como C), essa é a única maneira de inicializar uma matriz
char
com um valor de sequência (a inicialização é diferente da atribuição). Você pode escreverou
ou
No primeiro caso, o tamanho da matriz é obtido do tamanho do inicializador. Literais de string são armazenados como matrizes
char
com um byte final de 0, portanto, o tamanho da matriz é 8 ('o', 'c', 't', 'o', 'b', 'e', 'r', 0) Nos dois segundos casos, o tamanho da matriz é especificado como parte da declaração (8 eMAX_MONTH_LENGTH
, o que quer que seja).O que você não pode fazer é escrever algo como
ou
etc. No primeiro caso, a declaração de
string
está incompleta porque nenhum tamanho de matriz foi especificado e não há inicializador para o tamanho. Nos dois casos,=
isso não funcionará porque a) uma expressão de matriz, comostring
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 de qualquer maneira.Da mesma maneira, você não pode escrever
Onde
foo
está outra matriz dechar
. Essa forma de inicialização funcionará apenas com literais de string.EDITAR
Devo alterar isso para dizer que você também pode inicializar matrizes para conter uma string com um inicializador no estilo de matriz, como
ou
mas é mais fácil para os olhos usar literais de string.
EDIT 2
Para atribuir o conteúdo de uma matriz fora de uma declaração, você precisará usar
strcpy/strncpy
(para cadeias terminadas em 0) oumemcpy
(para qualquer outro tipo de matriz):ou
fonte
strncpy
raramente é a resposta certachar[8] str = "october";
é uma má prática. Eu tive que literalmente me contar para ter certeza de que não havia transbordamento e quebras sob manutenção ... por exemplo, corrigir um erro ortográfico deseprate
paraseparate
quebrará se o tamanho não for atualizado.strlen()
não incluir o carácter nulo, usandoMAX_MONTH_LENGTH
para manter o tamanho máximo necessário parachar string[]
muitas vezes parece . Errado IMO,MAX_MONTH_SIZE
seria melhor aqui.O único problema que recordo é atribuir literal de cadeia a
char *
:Por exemplo, pegue este programa:
Isso na minha plataforma (Linux) falha ao tentar gravar na página marcada como somente leitura. Em outras plataformas, pode imprimir 'setembro' etc.
Dito isto - a inicialização por literal faz a quantidade específica de reserva para que isso não funcione:
Mas isso vai
Como última observação - eu não usaria
strcpy
nada:Enquanto alguns compiladores podem transformá-lo em chamada segura,
strncpy
é muito mais seguro:fonte
strncpy
porque ele não encerra a seqüência de caracteres nula quando o comprimento desomething_else
é maior quesizeof(buf)
. Normalmente, defino o último caracterebuf[sizeof(buf)-1] = 0
a ser protegido ou, sebuf
for inicializado com zero, usesizeof(buf) - 1
como comprimento da cópia.strlcpy
oustrcpy_s
ou mesmosnprintf
se for necessário.strlcpy
esnprintf
não esteja diretamente acessível no MSVC, pelo menos pedidos estrcpy_s
não no * nix).Uma coisa que nenhum dos tópicos traz é o seguinte:
vs.
O primeiro fará algo como:
O último faz apenas o memcpy. O padrão C insiste que, se qualquer parte de uma matriz é inicializada, tudo é. Portanto, neste caso, é melhor fazer você mesmo. Eu acho que pode ter sido o que Treuss estava chegando.
Com certeza
é melhor que:
ou
ps Para pontos de bônus, você pode:
para lançar um tempo de compilação dividido por zero de erro se você estiver prestes a sobrecarregar a matriz.
fonte
Principalmente porque você não terá o tamanho de
char[]
uma variável / construção que você pode usar facilmente dentro do programa.O exemplo de código do link:
string
é alocado na pilha com 7 ou 8 caracteres. Não me lembro se é terminado por nulo dessa maneira ou não - o segmento ao qual você vinculou afirmou que é.Copiar "setembro" sobre essa sequência é um óbvio excesso de memória.
Outro desafio ocorre se você passar
string
para outra função para que a outra função possa gravar na matriz. Você precisa dizer a outra função de quanto tempo a matriz é tão -lo não cria uma superação. Você pode transmitirstring
o resultado de,strlen()
mas o thread explica como isso pode explodir sestring
não for terminado por nulo.É melhor alocar uma string com um tamanho fixo (de preferência definido como 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 existem maneiras de atenuar esses riscos. Eles também exigem mais esforço de sua parte para usá-los.
Na minha experiência, o valor que eu inicializei
char[]
é geralmente muito pequeno para os outros valores que eu preciso colocar lá. O uso de 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 destrlen
se preocupar com memória.Da mesma forma, você pode fazer um teste antes da chamada para
strcpy
ver se o seu buffer de destino é grande o suficiente para a cadeia de origem:if (sizeof target > strlen(src)) { strcpy (target, src); }
.Sim, se você tem que passar a matriz para uma função, você precisa passar seu tamanho físico, bem como:
foo (array, sizeof array / sizeof *array);
. - John Bodefonte
sizeof string
fornecerá o tamanho do buffer (8 bytes); use o resultado dessa expressão em vez destrlen
quando estiver preocupado com a memória. Da mesma forma, você pode fazer um teste antes da chamada parastrcpy
ver se o seu buffer de destino é grande o suficiente para a cadeia de origem:if (sizeof target > strlen(src)) { strcpy (target, src); }
. Sim, se você tem que passar a matriz para uma função, você precisa passar seu tamanho físico, bem como:foo (array, sizeof array / sizeof *array);
.string
resulta em uma conversão implícita emchar*
, apontando para o primeiro elemento da matriz. Isso perde as informações dos limites da matriz. Uma chamada de função é apenas um dos muitos contextos em que isso acontece.char *ptr = string;
é outro. Evenstring[0]
é um exemplo disso; o[]
operador trabalha em ponteiros, não diretamente em matrizes. Sugestão de leitura: Secção 6 do FAQ comp.lang.c .Eu acho que a idéia de "má prática" vem do fato de que esta forma:
cria implicitamente um strcpy do código da máquina de origem para a pilha.
É mais eficiente manipular apenas um link para essa sequência. Como com:
ou diretamente:
(mas é claro que na maioria dos códigos provavelmente não importa)
fonte
char time_buf[] = "00:00";
que você modifica um buffer? Umachar *
inicializada para um literal de cadeia é definida como o endereço do primeiro byte, portanto, tentar modificá-lo resulta em comportamento indefinido, porque o método de armazenamento da literal de cadeia é desconhecido (implementação definida), enquanto a modificação dos bytes de achar[]
é perfeitamente legal porque a inicialização copia os bytes para um espaço gravável alocado na pilha. Dizer que é "menos eficiente" ou "má prática" sem elaborar as nuances dechar* vs char[]
é enganoso.Nunca é realmente muito tempo, mas você deve evitar a inicialização char [] para string, porque "string" é const char * e está sendo atribuída a char *. Portanto, se você passar esse caractere [] para o método que altera os dados, poderá ter um comportamento interessante.
Como recomendado, misturei um pouco char [] com char *, isso não é bom, pois difere um pouco.
Não há nada errado em atribuir dados à matriz de caracteres, mas como a intenção de usá-la é usá-la como 'string' (caractere *), é fácil esquecer que você não deve modificar essa matriz.
fonte
const
menos que você o defina dessa maneira. (E os literais de string em C não sãoconst
, embora qualquer tentativa de modificar um literal de string tenha um comportamento indefinido.)char *s = "literal";
Tem o tipo de comportamento que você está falando; é melhor escrito comoconst char *s = "literal";
const
onint n = 42;
, porque42
é uma constante.c
é modificável. É exatamente uma garantia tão forte quanto a que é1 + 1
avaliada2
. Se o programa ao qual vinculei acima fizer algo diferente de imprimirEFGH
, isso indica uma implementação C não conforme.