Estou tentando entender os ponteiros em C, mas atualmente estou confuso com o seguinte:
char *p = "hello"
Este é um ponteiro de caractere apontando para a matriz de caracteres, começando em h .
char p[] = "hello"
Essa é uma matriz que armazena oi .
Qual é a diferença quando passo essas duas variáveis para essa função?
void printSomething(char *p)
{
printf("p: %s",p);
}
char p[3] = "hello";
a cadeia de inicialização é muito longa para o tamanho da matriz que você declara. Erro de digitação?char p[]="hello";
seria suficiente!char
específico.Respostas:
char*
echar[]
são tipos diferentes , mas não é imediatamente aparente em todos os casos. Isso ocorre porque as matrizes se decompõem em ponteiros , o que significa que, se uma expressão de tipochar[]
for fornecida ondechar*
é esperado um tipo , o compilador converte automaticamente a matriz em um ponteiro para o seu primeiro elemento.Seu exemplo de função
printSomething
espera um ponteiro, portanto, se você tentar passar um array para ele assim:O compilador finge que você escreveu isso:
fonte
printf
lida com a%s
cadeia de formato: inicie no endereço fornecido e continue até encontrar o terminador nulo. Se você quiser imprimir apenas um caractere, poderá usar a%c
string de formato, por exemplo.char *p = "abc";
o caractere NULL\0
é automaticamente anexado, como no caso do array char []?char *name; name="123";
mas posso fazer o mesmo com oint
tipo? E depois de usar%c
para imprimirname
, a saída é uma sequência ilegível�
:?Vamos ver:
foo * e foo [] são tipos diferentes e são tratados de forma diferente pelo compilador (ponteiro = endereço + representação do tipo do ponteiro, matriz = ponteiro + comprimento opcional da matriz, se conhecido, por exemplo, se a matriz estiver alocada estaticamente ), os detalhes podem ser encontrados no padrão. E no nível do tempo de execução, não há diferença entre eles (em assembler, bem, quase, veja abaixo).
Além disso, há uma pergunta relacionada na C FAQ :
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
Você não tem permissão para alterar o conteúdo de uma constante de string, que é o que o primeiro
p
aponta. O segundop
é uma matriz inicializada com uma constante de sequência e você pode alterar seu conteúdo.fonte
Em casos como esse, o efeito é o mesmo: você acaba passando o endereço do primeiro caractere em uma sequência de caracteres.
Obviamente, as declarações não são as mesmas.
A seguir, a memória é reservada para uma seqüência de caracteres e também para um ponteiro de caractere e, em seguida, inicializa o ponteiro para apontar para o primeiro caractere na seqüência de caracteres.
Enquanto o seguinte separa a memória apenas para a string. Portanto, ele pode realmente usar menos memória.
fonte
Tanto quanto me lembro, uma matriz é na verdade um grupo de indicadores. Por exemplo
é uma afirmação verdadeira
fonte
*(arr + 1)
leva ao segundo membro dearr
. Se*(arr)
aponta para um endereço de memória de 32 bits, por exemplobfbcdf5e
,*(arr + 1)
aponta parabfbcdf60
(o segundo byte). Portanto, por que sair do escopo de uma matriz levará a resultados estranhos se o sistema operacional não fizer um defeito. Seint a = 24;
estiver no endereçobfbcdf62
, o acessoarr[2]
poderá retornar24
, assumindo que um segfault não ocorra primeiro.No APUE , Seção 5.14:
O texto citado corresponde à explicação de @Ciro Santilli.
fonte
char p[3] = "hello"
? deve serchar p[6] = "hello"
lembrar que há um '\ 0' char no final de uma "string" em C.de qualquer maneira, a matriz em C é apenas um ponteiro para o primeiro objeto de um objeto de ajuste na memória. os únicos s diferentes estão na semântica. enquanto você pode alterar o valor de um ponteiro para apontar para um local diferente na memória, uma matriz, depois de criada, sempre apontará para o mesmo local.
Além disso, ao usar o array, o "novo" e o "excluir" são feitos automaticamente para você.
fonte