Em C, pode-se usar uma string literal em uma declaração como esta:
char s[] = "hello";
ou assim:
char *s = "hello";
Então qual é a diferença? Quero saber o que realmente acontece em termos de duração do armazenamento, tanto em tempo de compilação quanto em tempo de execução.
Respostas:
A diferença aqui é que
será colocado
"Hello world"
nas partes somente leitura da memória e fazers
um ponteiro para isso torna ilegal qualquer operação de gravação nessa memória.Enquanto estiver fazendo:
coloca a string literal na memória somente leitura e copia a string para a memória recém-alocada na pilha. Fazendo
legal.
fonte
"Hello world"
está em "partes somente leitura da memória" nos dois exemplos. O exemplo com a matriz aponta para lá, o exemplo com a matriz copia os caracteres para os elementos da matriz.char msg[] = "hello, world!";
a string e termina na seção de dados inicializados. Quando declaradochar * const
para terminar na seção de dados somente leitura. gcc-4.5.3Primeiro, nos argumentos das funções, eles são exatamente equivalentes:
Em outros contextos,
char *
aloca um ponteiro, enquantochar []
aloca uma matriz. Para onde vai a corda no primeiro caso, você pergunta? O compilador aloca secretamente uma matriz anônima estática para manter a string literal. Assim:Observe que você nunca deve tentar modificar o conteúdo dessa matriz anônima por meio desse ponteiro; os efeitos são indefinidos (geralmente significando uma falha):
O uso da sintaxe da matriz aloca-a diretamente na nova memória. Assim, a modificação é segura:
No entanto, a matriz vive apenas enquanto seu escopo contaning, portanto, se você fizer isso em uma função, não retorne ou vaze um ponteiro para essa matriz - faça uma cópia em vez de com
strdup()
ou similar. Se a matriz estiver alocada no escopo global, é claro, não há problema.fonte
Esta declaração:
Cria um objeto - uma
char
matriz de tamanho 6, chamadas
, inicializada com os valores'h', 'e', 'l', 'l', 'o', '\0'
. O local em que essa matriz está alocada na memória e por quanto tempo ela depende depende de onde a declaração aparece. Se a declaração estiver dentro de uma função, ela permanecerá até o final do bloco em que foi declarada e quase certamente será alocada na pilha; se estiver fora de uma função, provavelmente será armazenado em um "segmento de dados inicializado" carregado do arquivo executável na memória gravável quando o programa for executado.Por outro lado, esta declaração:
Cria dois objetos:
char
s contendo os valores'h', 'e', 'l', 'l', 'o', '\0'
, que não tem nome e tem duração de armazenamento estático (o que significa que ele permanece por toda a vida útil do programa); es
, que é inicializada com o local do primeiro caractere nessa matriz sem nome e somente leitura.A matriz somente leitura sem nome geralmente está localizada no segmento "texto" do programa, o que significa que é carregada do disco na memória somente leitura, junto com o próprio código. A localização da
s
variável ponteiro na memória depende de onde a declaração aparece (como no primeiro exemplo).fonte
char s[] = "hello"
caso, o"hello"
é apenas um inicializador dizendo ao compilador como a matriz deve ser inicializada. Pode ou não resultar em uma sequência correspondente no segmento de texto - por exemplo, ses
tiver duração de armazenamento estático, é provável que a única instância"hello"
esteja no segmento de dados inicializado - os
próprio objeto . Mesmo ques
tenha duração de armazenamento automático, ele pode ser inicializado por uma sequência de armazenamentos literais, em vez de uma cópia (por exemplomovl $1819043176, -6(%ebp); movw $111, -2(%ebp)
)..rodata
qual o script do vinculador despeja no mesmo segmento que.text
. Veja minha resposta .char s[] = "Hello world";
coloca a string literal na memória somente leitura e copia a string para a memória recém-alocada na pilha. Mas, a sua resposta só fala sobre o put string literal na memória só de leitura e ignora a segunda parte da frase que diz:copies the string to newly allocated memory on the stack
. Então, sua resposta está incompleta por não especificar a segunda parte?char s[] = "Hellow world";
é apenas um inicializador e não é necessariamente armazenada como uma cópia somente leitura separada. Ses
tiver duração de armazenamento estático, é provável que a única cópia da string esteja em um segmento de leitura e gravação no local des
, e mesmo se não houver, o compilador pode optar por inicializar a matriz com instruções de carregamento imediato ou similar, em vez de copiar de uma sequência de somente leitura. O ponto é que, nesse caso, a própria string do inicializador não tem presença de tempo de execução.Dadas as declarações
assuma o seguinte mapa de memória hipotética:
A literal de cadeia de caracteres
"hello world"
é uma matriz de 12 elementoschar
(const char
em C ++) com duração de armazenamento estático, o que significa que a memória é alocada quando o programa é iniciado e permanece alocada até o término do programa. Tentar modificar o conteúdo de uma cadeia de caracteres literal invoca um comportamento indefinido.A linha
define
s0
como um ponteiro parachar
a duração do armazenamento automático (ou seja, a variávels0
existe apenas para o escopo em que é declarada) e copia o endereço da string literal (0x00008000
neste exemplo) para ela. Note-se que desde ques0
aponta para um literal de cadeia, ele não deve ser usado como um argumento para qualquer função que iria tentar modificá-lo (por exemplo,strtok()
,strcat()
,strcpy()
, etc.).A linha
define
s1
como uma matriz de 12 elementos dechar
(o comprimento é retirado da string literal) com duração de armazenamento automático e copia o conteúdo da literal para a matriz. Como você pode ver no mapa da memória, temos duas cópias da string"hello world"
; a diferença é que você pode modificar a string contida ems1
.s0
es1
são intercambiáveis na maioria dos contextos; Aqui estão as exceções:Você pode reatribuir a variável
s0
para apontar para uma literal de seqüência de caracteres diferente ou para outra variável. Você não pode reatribuir a variávels1
para apontar para uma matriz diferente.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
.Observe, no entanto, que o script vinculador padrão coloca
.rodata
e.text
no mesmo segmento , que possui permissão de gravação, mas não possui permissão de gravação. Isso pode ser observado com:que contém:
Se fizermos o mesmo para
char[]
:nós obtemos:
para que seja armazenado na pilha (em relação a
%rbp
).fonte
declara
s
ser uma matrizchar
longa o suficiente para armazenar o inicializador (5 + 1char
s) e inicializa a matriz copiando os membros da string especificada literalmente na matriz.declara
s
ser um ponteiro para um ou mais (nesse caso, mais) sechar
aponta diretamente para um local fixo (somente leitura) que contém o literal"hello"
.fonte
s
é um ponteiro paraconst char
.Aqui
s
está uma matriz de caracteres, que podem ser substituídos, se assim o desejarmos.Um literal de seqüência de caracteres é usado para criar esses blocos de caracteres em algum lugar da memória para o qual esse ponteiro
s
está apontando. Podemos aqui reatribuir o objeto para o qual ele está apontando alterando isso, mas desde que aponte para uma string literal, o bloco de caracteres para o qual ele aponta não pode ser alterado.fonte
Além disso, considere que, para fins de somente leitura, o uso de ambos é idêntico, você pode acessar um char indexando com
[]
ou no*(<var> + <index>)
formato:E:
Obviamente, se você tentar fazer
Você provavelmente receberá uma falha de segmentação, ao tentar acessar a memória somente leitura.
fonte
x[1] = 'a';
que ocorrerá falha também (dependendo da plataforma, é claro).Apenas para adicionar: você também obtém valores diferentes para seus tamanhos.
Como mencionado acima, para uma matriz
'\0'
será alocado como o elemento final.fonte
Os conjuntos acima str para apontar para o valor literal "Hello", que é codificado na imagem binária do programa, que é sinalizada como somente leitura na memória, significa que qualquer alteração nesse literal String é ilegal e causaria falhas de segmentação.
copia a string para a memória recém-alocada na pilha. Assim, qualquer alteração é permitida e legal.
vai mudar o str para "Mello".
Para mais detalhes, consulte a pergunta semelhante:
Por que recebo uma falha de segmentação ao escrever em uma string inicializada com "char * s", mas não "char s []"?
fonte
No caso de:
x é um valor l - ele pode ser atribuído. Mas no caso de:
x não é um lvalue, é um rvalue - você não pode atribuir a ele.
fonte
x
é um valor não modificável. Em quase todos os contextos, porém, ele será avaliado como um ponteiro para seu primeiro elemento, e esse valor é um rvalor.fonte
À luz dos comentários aqui, deve ser óbvio que: char * s = "olá"; É uma péssima idéia e deve ser usada em escopo muito restrito.
Essa pode ser uma boa oportunidade para apontar que a "correção constante" é uma "coisa boa". Sempre e onde você puder, use a palavra-chave "const" para proteger seu código, de chamadores ou programadores "relaxados", que geralmente são mais "relaxados" quando os ponteiros entram em ação.
Chega de melodrama, eis o que se pode obter ao decorar ponteiros com "const". (Nota: É necessário ler as declarações dos ponteiros da direita para a esquerda.) Aqui estão as três maneiras diferentes de se proteger ao brincar com os ponteiros:
- ou seja, o objeto DBJ não pode ser alterado via p.
- ou seja, você pode alterar o objeto DBJ via p, mas não pode alterar o ponteiro p.
- ou seja, você não pode alterar o ponteiro p, nem o objeto DBJ via p.
Os erros relacionados à tentativa de mutação const-form são detectados em tempo de compilação. Não há espaço de tempo de execução ou penalidade de velocidade para const.
(Suponha que você esteja usando o compilador C ++, é claro?)
--DBJ
fonte