pointer - c char array operations



Qual é a diferença entre o array char vs o ponteiro char em C? (6)

Eu estou tentando entender ponteiros em C, mas atualmente estou confuso com o seguinte:

  • char *p = "hello"

    Este é um ponteiro de char apontando para a matriz de caracteres, começando em h .

  • char p[] = "hello"

    Esta é uma matriz que armazena o olá .

Qual é a diferença quando eu passo essas duas variáveis ​​para essa função?

void printSomething(char *p)
{
    printf("p: %s",p);
}

Answer #1

Qual é a diferença entre o array char vs o ponteiro char em C?

C99 N1256 draft

Existem dois usos diferentes de literais de string de caracteres:

  1. Inicializar char[] :

    char c[] = "abc";      

    Isso é "mais mágica" e descrito em 6.7.8 / 14 "Inicialização":

    Uma matriz de tipo de caractere pode ser inicializada por um literal de cadeia de caracteres, opcionalmente entre chaves. Caracteres sucessivos do literal de cadeia de caracteres (incluindo o caractere nulo de terminação, se houver espaço ou se o array for de tamanho desconhecido) inicializam os elementos do array.

    Então isso é apenas um atalho para:

    char c[] = {'a', 'b', 'c', '\0'};

    Como qualquer outro array regular, c pode ser modificado.

  2. Em toda parte: gera um:

    Então, quando você escreve:

    char *c = "abc";

    Isso é semelhante a:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;

    Observe a conversão implícita de char[] para char * , que é sempre legal.

    Então, se você modificar c[0] , também modifica __unnamed , que é UB.

    Isso está documentado em 6.4.5 "literais de seqüência de caracteres":

    5 Na fase de tradução 7, um byte ou código de valor zero é anexado a cada sequência de caracteres multibyte que resulta de uma literal de cadeia ou literais. A seqüência de caracteres multibyte é então usada para inicializar uma matriz de duração e comprimento de armazenamento estáticos apenas o suficiente para conter a seqüência. Para literais de cadeia de caracteres, os elementos da matriz têm o tipo char e são inicializados com os bytes individuais da seqüência de caracteres multibyte [...]

    6 Não é especificado se esses arrays são distintos, desde que seus elementos tenham os valores apropriados. Se o programa tentar modificar essa matriz, o comportamento é indefinido.

6.7.8 / 32 "Inicialização" dá um exemplo direto:

EXEMPLO 8: A declaração

char s[] = "abc", t[3] = "abc";

define objetos de matriz char "simples" s e t cujos elementos são inicializados com literais de cadeia de caracteres.

Esta declaração é idêntica à

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

O conteúdo das matrizes é modificável. Por outro lado, a declaração

char *p = "abc";

define p com tipo "pointer to char" e inicializa para apontar para um objeto com o tipo "array de char" com comprimento 4 cujos elementos são inicializados com um literal de string de caractere. Se for feita uma tentativa de usar p para modificar o conteúdo da matriz, o comportamento é indefinido.

Implementação do GCC 4.8 x86-64 ELF

Programa:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compile e descompile:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Saída contém:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusão: O GCC armazena o char* it na seção .rodata , não no .text .

Se fizermos o mesmo para o char[] :

 char s[] = "abc";

nós obtemos:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

então fica armazenado na pilha (em relação a %rbp ).

No entanto, observe que o script de vinculador padrão coloca .rodata e .text no mesmo segmento, que possui permissão de gravação, mas não de gravação. Isso pode ser observado com:

readelf -l a.out

que contém:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

Answer #2

De APUE , Seção 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

... Para o primeiro modelo, o nome é alocado na pilha, porque usamos uma variável de matriz. Para o segundo nome, no entanto, usamos um ponteiro. Nesse caso, somente a memória para o ponteiro reside na pilha; o compilador faz com que a string seja armazenada no segmento somente leitura do executável. Quando a função mkstemp tenta modificar a seqüência, ocorre uma falha de segmentação.

O texto citado corresponde à explicação do @Ciro Santilli.


Answer #3

Tanto quanto me lembro, um array é na verdade um grupo de ponteiros. Por exemplo

p[1]== *(&p+1)

é uma afirmação verdadeira


Answer #4

Vamos ver:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo * e foo [] são tipos diferentes e são tratados de maneira diferente pelo compilador (ponteiro = endereço + representação do tipo do ponteiro, array = ponteiro + comprimento opcional da matriz, se conhecido, por exemplo, se a matriz é alocada estaticamente ), os detalhes podem ser encontrados no padrão. E no nível de tempo de execução não há diferença entre eles (em assembler, bem, quase, veja abaixo).

Além disso, há uma question relacionada no C FAQ :

P : Qual é a diferença entre essas inicializações?

char a[] = "string literal";   
char *p  = "string literal";   

Meu programa trava se eu tentar atribuir um novo valor a p [i].

R : Um literal de string (o termo formal para uma string de aspas duplas em fonte C) pode ser usado de duas maneiras ligeiramente diferentes:

  1. Como o inicializador de uma matriz de char, como na declaração de char a [], especifica os valores iniciais dos caracteres nessa matriz (e, se necessário, seu tamanho).
  2. Em qualquer outro lugar, ele se transforma em um array de caracteres estáticos, sem nome, e esse array sem nome pode ser armazenado em memória somente leitura e, portanto, não pode necessariamente ser modificado. Em um contexto de expressão, a matriz é convertida de uma vez para um ponteiro, como de costume (consulte a seção 6), portanto, a segunda declaração inicializa p para apontar para o primeiro elemento da matriz sem nome.

Alguns compiladores possuem um switch que controla se literais de string são graváveis ​​ou não (para compilar código antigo), e alguns podem ter opções para fazer com que literais de string sejam formalmente tratados como arrays de const char (para melhor captura de erros).

Veja também as perguntas 1.31, 6.1, 6.2, 6.8 e 11.8b.

Referências: K & R2 Sec. 5,5 p. 104

ISO Sec. 6.1.4, cap. 6.5.7

Justificativa Sec. 3.1.4

H & S Sec. 2.7.4 pp. 31-2


Answer #5

char p[3] = "hello" ? deve ser char p[6] = "hello" lembre-se que existe um caractere '\ 0' no final de uma "string" em C.

De qualquer forma, array em C é apenas um ponteiro para o primeiro objeto de um ajuste de objetos 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.
também ao usar array, o "novo" e o "delete" são feitos automaticamente para você.


Answer #6

char* e char[] são tipos diferentes , mas não são imediatamente aparentes em todos os casos. Isso ocorre porque as matrizes se decompõem em ponteiros , o que significa que, se uma expressão do tipo char[] for fornecida, onde o tipo char* é esperado, o compilador converterá automaticamente a matriz em um ponteiro para seu primeiro elemento.

Sua função de exemplo printSomething espera um ponteiro, então se você tentar passar um array para ele da seguinte forma:

char s[10] = "hello";
printSomething(s);

O compilador finge que você escreveu isto:

char s[10] = "hello";
printSomething(&s[0]);




pointers