O código a seguir recebe a falha seg na linha 2:
char *str = "string";
str[0] = 'z'; // could be also written as *str = 'z'
printf("%s\n", str);
Enquanto isso funciona perfeitamente bem:
char str[] = "string";
str[0] = 'z';
printf("%s\n", str);
Testado com MSVC e GCC.
c
segmentation-fault
c-strings
Markus
fonte
fonte
Respostas:
Consulte as Perguntas frequentes sobre C, pergunta 1.32
fonte
mprotect
para acenar a proteção somente leitura (veja aqui ).Normalmente, literais de seqüência de caracteres são armazenados na memória somente leitura quando o programa é executado. Isso evita que você altere acidentalmente uma constante de string. No seu primeiro exemplo,
"string"
é armazenado na memória somente leitura e*str
aponta para o primeiro caractere. O segfault acontece quando você tenta alterar o primeiro caractere para'z'
.No segundo exemplo, a seqüência de caracteres
"string"
é copiada pelo compilador de sua casa somente leitura para astr[]
matriz. É permitido alterar o primeiro caractere. Você pode verificar isso imprimindo o endereço de cada um:Além disso, imprimir o tamanho de
str
no segundo exemplo mostrará que o compilador alocou 7 bytes para ele:fonte
%zu
para imprimirsize_t
A maioria dessas respostas está correta, mas apenas para adicionar um pouco mais de clareza ...
A "memória somente leitura" a que as pessoas estão se referindo é o segmento de texto em termos ASM. É o mesmo lugar na memória em que as instruções são carregadas. Isso é somente leitura por razões óbvias, como segurança. Quando você cria um char * inicializado em uma string, os dados da string são compilados no segmento de texto e o programa inicializa o ponteiro para apontar para o segmento de texto. Então, se você tentar mudar, kaboom. Segfault.
Quando gravado como uma matriz, o compilador coloca os dados da string inicializada no segmento de dados, que é o mesmo local em que vivem suas variáveis globais. Essa memória é mutável, pois não há instruções no segmento de dados. Desta vez, quando o compilador inicializa a matriz de caracteres (que ainda é apenas um caractere *), está apontando para o segmento de dados em vez do segmento de texto, que você pode alterar com segurança no tempo de execução.
fonte
C99 N1256 draft
Existem dois usos diferentes dos literais da cadeia de caracteres:
Inicialize
char[]
:Isso é "mais mágico" e descrito em 6.7.8 / 14 "Inicialização":
Portanto, este é apenas um atalho para:
Como qualquer outra matriz regular,
c
pode ser modificado.Em qualquer outro lugar: gera um:
Então, quando você escreve:
Isso é semelhante a:
Observe a conversão implícita de
char[]
parachar *
, que é sempre legal.Então, se você modificar
c[0]
, também modifique__unnamed
, que é UB.Isso está documentado em 6.4.5 "String literals":
6.7.8 / 32 "Inicialização" dá um exemplo direto:
Implementação do GCC 4.8 x86-64 ELF
Programa:
Compilar e descompilar:
A saída contém:
Conclusão: o GCC o armazena
char*
em.rodata
seção, não em.text
.Se fizermos o mesmo para
char[]
:nós obtemos:
para que seja armazenado na pilha (em relação a
%rbp
).Observe, no entanto, que o script do vinculador padrão coloca
.rodata
e.text
no mesmo segmento, que tem execução, mas não possui permissão de gravação. Isso pode ser observado com:que contém:
fonte
No primeiro código, "string" é uma constante de string, e as constantes de string nunca devem ser modificadas porque são frequentemente colocadas na memória somente leitura. "str" é um ponteiro usado para modificar a constante.
No segundo código, "string" é um inicializador de array, uma espécie de mão curta para
"str" é uma matriz alocada na pilha e pode ser modificada livremente.
fonte
str
é global oustatic
.Porque o tipo de
"whatever"
no contexto do 1º exemplo éconst char *
(mesmo que você o atribua a um caractere não-const *), o que significa que você não deve tentar escrever nele.O compilador aplicou isso colocando a string em uma parte de memória somente leitura, portanto, a gravação nela gera um segfault.
fonte
Para entender esse erro ou problema, você deve primeiro saber a diferença entre o ponteiro e o array, então aqui primeiro eu explico as diferenças entre eles
matriz de string
Na memória matriz é armazenada em células de memória contínuos, armazenado como
[h][e][l][l][o][\0] =>[]
é célula de memória tamanho byte um carvão animal, e esta células de memória contínuos pode ser acesso por nome com o nome strArray here.so aqui matriz de cadeiastrarray
si contendo todos os caracteres de cadeia inicializado para este it.In caso aqui,"hello"
para que possamos alterar facilmente seu conteúdo de memória, acessando cada caractere por seu valor de índicee seu valor foi alterado para
'm'
um valor tão restrito alterado para"mello"
;Um ponto a ser observado aqui é que podemos alterar o conteúdo da matriz de string alterando caractere por caractere, mas não podemos inicializar outra string diretamente para ela, como
strarray="new string"
inválida.Ponteiro
Como todos sabemos, o ponteiro aponta para o local da memória na memória, o ponteiro não inicializado aponta para o local aleatório da memória e, após a inicialização, aponta para o local específico da memória
aqui o ponteiro ptr é inicializado para uma string
"hello"
que é uma string constante armazenada na memória somente leitura (ROM), portanto"hello"
não pode ser alterada, pois é armazenada na ROMe ptr é armazenado na seção da pilha e apontando para a string constante
"hello"
então ptr [0] = 'm' é inválido, pois você não pode acessar a memória somente leitura
Mas ptr pode ser inicializado para outro valor de string diretamente, pois é apenas um ponteiro, para que possa ser apontado para qualquer endereço de memória da variável do seu tipo de dados
fonte
Os itens acima
str
apontam para o valor literal"string"
codificado na imagem binária do programa, que provavelmente é sinalizada como somente leitura na memória.Assim,
str[0]=
está tentando gravar no código somente leitura do aplicativo. Eu acho que isso provavelmente é dependente do compilador.fonte
aloca um ponteiro para uma string literal, que o compilador está colocando em uma parte não modificável do seu executável;
aloca e inicializa uma matriz local que é modificável
fonte
int *b = {1,2,3)
como escrevemoschar *s = "HelloWorld"
?O FAQ C que o @matli vinculou menciona, mas mais ninguém aqui o fez, então, para esclarecimentos: se uma literal de string (string com aspas duplas na sua fonte) for usada em outro lugar que não seja para inicializar uma matriz de caracteres (por exemplo: @ O segundo exemplo de Mark, que funciona corretamente), que a string é armazenada pelo compilador em uma tabela estática especial , semelhante à criação de uma variável estática global (somente leitura, é claro) que é essencialmente anônima (não tem nome "variável" "). A parte somente leitura é a parte importante e é por isso que o primeiro exemplo de código do @ Mark é segmentado.
fonte
int *b = {1,2,3)
como escrevemoschar *s = "HelloWorld"
?o
A linha define um ponteiro e o aponta para uma string literal. A cadeia literal não é gravável, portanto, quando você faz:
você recebe uma falha seg. Em algumas plataformas, o literal pode estar na memória gravável para que você não veja um segfault, mas é um código inválido (resultando em comportamento indefinido) independentemente.
A linha:
aloca uma matriz de caracteres e copia a string literal para essa matriz, que é totalmente gravável, para que a atualização subsequente não seja um problema.
fonte
int *b = {1,2,3)
como escrevemoschar *s = "HelloWorld"
?Literais de string como "string" provavelmente são alocados no espaço de endereço do executável como dados somente leitura (fornecer ou receber seu compilador). Quando você vai tocá-lo, fica assustado que você esteja na área de roupas de banho e avisa com uma falha de seg.
No seu primeiro exemplo, você está recebendo um ponteiro para esses dados const. No seu segundo exemplo, você está inicializando uma matriz de 7 caracteres com uma cópia dos dados const.
fonte
fonte
Em primeiro lugar,
str
é um ponteiro que aponta para"string"
. É permitido ao compilador colocar literais de seqüência de caracteres em lugares na memória em que você não pode gravar, mas pode apenas ler. (Isso realmente deveria ter acionado um aviso, já que você está atribuindoconst char *
a achar *
. Você tinha avisos desativados ou apenas os ignorou?)Em segundo lugar, você está criando uma matriz, que é a memória à qual você tem acesso total e inicializando-a
"string"
. Você está criando umchar[7]
(seis para as letras, um para o final '\ 0') e faz o que quiser com ele.fonte
const
prefixo Yes torna variáveis somentechar [N]
, o tipo não éconst char [N]
, portanto, não há aviso. (Você pode mudar isso no gcc, pelo menos pela passagem-Wwrite-strings
.)Suponha que as strings sejam,
No primeiro caso, o literal deve ser copiado quando 'a' entrar no escopo. Aqui 'a' é uma matriz definida na pilha. Isso significa que a string será criada na pilha e seus dados serão copiados da memória de código (texto), que geralmente é somente leitura (isso é específico da implementação, um compilador pode colocar esses dados do programa somente leitura na memória gravável também )
No segundo caso, p é um ponteiro definido na pilha (escopo local) e referente a uma string literal (dados ou texto do programa) armazenada em outro local. Normalmente, modificar essa memória não é uma boa prática nem incentivada.
fonte
Primeiro é uma string constante que não pode ser modificada. O segundo é uma matriz com valor inicializado, para que possa ser modificada.
fonte
A falha de segmentação é causada quando você tenta acessar a memória inacessível.
char *str
é um ponteiro para uma string que não é modificável (o motivo da obtenção do segfault).Considerando que
char str[]
é uma matriz e pode ser modificável.fonte