paulo1205
(usa Ubuntu)
Enviado em 02/01/2015 - 00:11h
danielcrvg escreveu:
( ... )
- Vetor de char vs String ..
As diferencas que eu entendi:
Strings sao definidas como char e os dados ficam entre " " e vetor de chars ' ', exemplo:
char nome[] = "daniel";
em quanto vetor de chars:
char nome[] = {'d','a'.'n','i','e','l'};
Nas strings, quando não se define o tamanho, o compilador automaticamente ja coloca o 'barra0' no final dela.
Na verdade, não é bem isso.
O fato incontornável é que C não possui um tipo específico para strings. Todo string em C
é um array de caracteres (podendo ser declarado como tal ou ser um bloco alocado dinamicamente e manipulado através de um ponteiro). A única coisa que distingue um vetor que contém um string de outro que é mera coleção de caracteres é a presença (ou a expectativa da presença) do terminador, que é um caráter de valor inteiro igual a zero.
char arr1[]="daniel";
char arr2[]={'d', 'a', 'n', 'i', 'e', 'l'};
char arr3[]={100, 97, 110, 105, 101, 108};
char arr4[]={'d', 'a', 'n', 'i', 'e', 'l', 0};
char arr3[]={100, 97, 110, 105, 101, 108, 0};
/* (sizeof arr1)==7, arr1 pode ser usado como string. */
/* (sizeof arr2)==6, arr2 não deve ser usado como string. */
/* (sizeof arr3)==6, arr3 não deve ser usado como string. */
/* Os conteúdos de arr2 e arr3 são idênticos. */
/* (sizeof arr4)==7, arr4 pode ser usado como string. */
/* (sizeof arr5)==7, arr5 pode ser usado como string. */
/*
Os conteúdos de arr1, arr4 e arr5 são idênticos, mas existe
uma diferença entre eles: o conteúdo de arr1 é constante, mesmo
tendo sido declarado sem a palavra-chave const (em C; se fosse
C++, a especificação de const na delcaração de arr1 seria
obrigatória).
*/
A sintaxe de texto entre aspas é uma conveniência oferecida pela linguagem para simplificar a definição e o uso de arrays de caracteres constantes destinados a representar strings. Os caracteres entre aspas são dispostos sequencialmente na memória, e automaticamente sucedidos por um caráter nulo. Além disso, o tipo de uma expressão como
"daniel" (incluindo as aspas) é
endereço (ou ponteiro) para caráter , com a informação adicional de que esse caráter é o primeiro de um bloco de sete caracteres que constituem o array.
Bom agora vem minha duvida:
char xxx[]="daniel";
int i = 0;
char *ptnome;
ptnome=&xxx;
Aqui você cometeu um erro. O certo seria fazer
ptnome=xxx . Num outro tópico aberto por você aqui no fórum, eu disse que é errado usar o operador de obtenção de endereço de variável (
& ) sobre o nome de um array, porque esse nome não constitui uma variável, mas sim uma representação constante de um endereço.
No entanto, provavelmente o seu código compilou e até possivelmente rodou. Então que estória é essa que eu estou contando, de que não pode fazer do jeito como você fez?
Bom, esse é um daqueles lugares em que o C é um tanto obscuro. Se não entender parte do que eu vou apresentar, não se acanhe nem desista do C (essas coisas obscuras têm sua utilidade no momento certo -- que geralmente não é quando se é iniciante).
Existe um caso em que é válido aplicar
& sobre algo declarado com aspecto de array, que é quando o objeto declarado é parâmetro de função. Veja o exemplo.
void func(char param[10]){
char arr[10]="Um string"; /* 9 caracteres + terminador. */
char *p1, *p2, *p3, *p4;
p1=arr; /* OK: ponteiro para char recebe endereço de char
(primeiro elemento do bloco indicado por “arr”). */
p2=&arr; /* Problema: não faz sentido usar & em constante,
mas o compilador permite. No entanto isso causa
um erro de tipo de dados. */
p3=param; /* OK: ponteiro para char recebe endereço de char. */
p4=¶m; /* Erro de tipo (apesar da forma de array usada na de-
claração, o tipo de param é char *). */
char (*pac1)[10], (*pac2)[10];
pac1=&arr; /* Problema: não faz sentido usar & em constante,
mas aqui não existe mais o erro de tipo. */
pac2=¶m; /* Erro de tipo (ver abaixo). */
char **ppc1, **ppc2;
ppc1=&arr; /* Problema: não faz sentido usar & em constante,
e ainda existe uma incompatibilidade de tipos. */
ppc2=¶m; /* OK: param tem seu próprio endereço e é um char *,
então ¶m é compatível com char **. */
printf("p1=%p\n", p1);
printf("p2=%p\n", p2);
printf("p3=%p\n", p3);
printf("p4=%p\n\n", p4);
printf("pac1=%p\n", pac1);
printf("pac2=%p\n\n", pac2);
printf("ppc1=%p\n", ppc1);
printf("ppc2=%p\n", ppc2);
}
A aparente doideira começa na já primeira linha: você diz que o parâmetro
param é um array de dez caracteres, mas o compilador entende que ele é um mero ponteiro para caracteres, e permite que você chame a função com um argumento array de caracteres de qualquer tamanho ou, na verdade, com qualquer ponteiro. Mas por quê?
Porque se o compilador exigisse que o argumento fosse declarado com o mesmo aspecto da declaração do parâmetro, isso impediria chamar a função com um argumento dinamicamente alocado. Dito de outro modo, se o fato de o parâmetro ter a forma de um array com dez elementos obrigasse os argumentos a serem arrays com dez argumentos, você não poderia ter o seguinte código -- ainda que ele seja logicamente válido.
char *dyn_array;
dyn_array=calloc(10, 1); /* (sizeof char)==1 por definição */
func(dyn_array); /* Chamo a função com um array dinâmico com
exatamente 10 elementos. Por quê não? */
Então, a declaração da função
func (), acima, acaba fazendo com que a forma de array seja só um comentário de luxo, e normalmente é usada somente quando o número esperado de elementos tem de ser fixo. Na prática, ela é funcionalmente idêntica ao que se teria se a declaração tivesse sido a seguinte.
void func(*param)
Um dos aspectos dessa equivalência é o fato de que
param passa a ser uma variável ponteiro dentro da função. Como tal, não apenas o seu valor pode ser manipulado e alterado, mas também possui um endereço próprio, que pode ser obtido com
& . No entanto, a opção pela sintaxe de array tem uma implicação mais profunda, não apenas nesse código, mas sobre a linguagem como um todo.
A implicação é esta: se um parâmetro de função pode ser declarado como array e, ainda assim, pode receber a aplicação do operador
& , então qualquer array, declarado em qualquer parte do programa (quer como parâmetro, quer como variável estática, quer como variável automática, quer como campo de uma estrutura), também pode. Nos casos em que o nome do array
X com
N elementos não indicar uma variável (porque é constante), então o endereço resultando da expressão
&X deve ser idêntico ao endereço da expressão
X ;
entretanto, o tipo de &X não será igual ao tipo de X , mas sim “ponteiro para array com N elementos do mesmo tipo que X[0] ” . Então, se você tiver
int arr_int[15];
, terá que
arr_int e
&arr_int designam o mesmo endereço, mas o tipo de
arr_int é “array com 15 elementos inteiros”, ao passo que
&arr_int é do tipo “ponteiro para array com 15 elementos inteiros”.
(Eu não sei se existe uma justificativa plausível para tal comportamento, definido pelo comitê de padronização da linguagem C. Lendo as memórias do falecido Dennis Ritchie, criador do C, parece que duas linhas que orientaram o projeto original da linguagem C e dos primeiros compiladores foram a consistência de operações entre os diferentes tipos de dados, incluindo arrays e estruturas, e a construção de compiladores simples, que não impusessem limitações ao usuário ao custo de aumentar muito sua complexidade. No entanto, eu não creio que os princípios de Ritchie se apliquem nesse caso: tanto para transformar array na lista de parâmetros em ponteiros quanto para tratar não-variáveis como se variáveis fossem, o compilador tem de ter código especializado. Minha opinião pessoal é de aceitação sem entusiasmo do primeiro caso e radicalmente contrária ao segundo, porque cria inconsistência e abre espaço para erros acidentais.)
Dentro da função, eu trabalhei com três tipos de ponteiros: ponteiro para caracteres (
char * ), ponteiro para arrays de caracteres com dez elementos (
char (*)[10] ) e ponteiro para ponteiro de caracteres (
char ** ), e experimentei fazer atribuições a eles com os valores de
arr ,
param ,
&arr e
¶m .
Por razões históricas, o C é relativamente leniente com atribuições entre ponteiros de tipos diferentes (e, às vezes, até entre ponteiros e inteiros). É possível que você não receba erro ou aviso nenhum ao tentar compilar a função
func () que eu mostrei. É comum que alertas sobre essas operações só sejam mostrados se você usar certas opções de compilação que servem para identificar e alertar sobre código potencialmente perigoso (com o GCC, eu geralmente recomendo as opções “-Wall”, que habilita todos os avisos, “-Werror”, que transforma os avisos em erros, o que obriga a tratar os avisos recebidos, “-O2”, que ajuda a detectar variáveis não inicializadas ou blocos de código redundantes, e, às vezes, “-pedantic”, que avisa sobre construções que, apesar de válidas, poderiam dar problema se usadas com outro compilador).
Se você tiver compilado (sem as opções de diagnóstico) e tido a curiosidade de chamar a função
func (), provavelmente viu uma saída parecida com a seguinte.
p1=0x7fffdc074900
p2=0x7fffdc074900
p3=0x4007b6
p4=0x7fffdc0748b8
pac1=0x7fffdc074900
pac2=0x7fffdc0748b8
ppc1=0x7fffdc074900
ppc2=0x7fffdc0748b8
É interessante notar que os valores dos ponteiros
p1 ,
p2 ,
pac1 e
ppc1 são idênticos, embora
p1 tenha sido obtido de
arr e os demais a partir de
&arr . Isso difere do que ocorreu com
p3 ,
p4 ,
pac2 e
ppc2 , em que o primeiro recebeu o valor de
param , que é uma cópia do ponteiro (na verdade, um array constante) usado para invocar a função, e os outros recebem o endereço de onde o valor recebido ficou guardado durante a execução da função.
Compilando com as opções de diagnóstico que alertam a respeito ou inibem o uso leniente de ponteiros, pode-se ver as inconsistências.
$ gcc -Wall -Werror -o lixo lixo.c -std=c11
lixo.c: In function ‘func’:
lixo.c:10:5: error: assignment from incompatible pointer type [-Werror]
p2=&arr; /* Problema: não faz sentido usar & em constante,
^
lixo.c:15:5: error: assignment from incompatible pointer type [-Werror]
p4=¶m; /* Erro de tipo. */
^
lixo.c:20:7: error: assignment from incompatible pointer type [-Werror]
pac2=¶m; /* OK: obtém o endereço onde a função guarda o
^
lixo.c:24:7: error: assignment from incompatible pointer type [-Werror]
ppc1=&arr; /* Problema: não faz sentido usar & em constante,
^
cc1: all warnings being treated as errors
Se você compilar o seu programa com as mesmas opções, vai obter indicações de erro semelhantes.
printf("ENDENTENDO O BASICO SOBRE PONTEIROS\n");
printf("\n\n.:[ STRINGS ]:.\n");
printf("\nConteudo de 'xxx' (%%s, xxx): %s\n",xxx);
printf("Endereco de 'xxx' (%%p, &xxx): %p\n",&xxx);
ptnome=&xxx;
printf("Endereço de 'ptnome' (%%p, &ptnome): %p\n",&ptnome);
printf("Conteudo de 'ptnome (%%s, ptnome)': %s\n",ptnome);
printf("Endereço que 'ptnome' aponta (%%p, ptnome): %p\n\n",ptnome);
int zzz=10;
int *ptzzz;
printf("\n.:[ INTEIROS ]:.\n");
printf("\nConteudo de 'zzz' (%%d, zzz): %d\n",zzz);
printf("Endereco de 'zzz' (%%p, &zzz): %p\n",&zzz);
ptzzz=&zzz;
printf("Endereço de 'ptzzz' (%%p, &ptzzz): %p\n",&ptzzz);
printf("Conteudo de 'ptzzz' (%%d, *ptzzz): %d\n",*ptzzz);
printf("Endereço que 'ptzzz' aponta (%%p, ptzzz): %p\n",ptzzz);
printf("\n--------------------------------\n");
printf("Primeiro elemento de 'xxx': %c\n",xxx[0]);
printf("Endereco do 1 elemento: %p\n",&xxx[0]);
printf("Endereco do vetor: %p\n",&xxx);
printf("\nImprimindo elementos da string com ponteiros: \n");
for (i=0;i<strlen(xxx);i++) {
printf("%c : %p\n",*ptnome++,&ptnome);
}
char nome2[6]={'d','a','n','i','e','l'};
char *ptnome2;
ptnome2=&nome2;
i = 0;
printf("\nImprimindo elementos do vetor de char com ponteiros: \n");
for (i=0;i<strlen(xxx);i++) {
printf("%c : %p\n",*ptnome2++,&ptnome2[i]);
Esse “++” depois de [i]ptnome2 parece estar sobrando. Não creio que ele se adeque ao que você quer imprimir.
[quote]
}
printf("\nTamanho de um char: %d\n",sizeof(char));
return 0;
}
Na primeira parte eu apenas imprimi na tela valores, enderecos de memoria, etc apenas para manipular os ponteiros..
Ai quando eu resolvi imprimir o vetor de char que me bateu a duvida:
Se o char tem apenas 1 byte, pq o seu endereco de memoria esta sendo de 2 em 2, ao inves de ser de 1 em 1 ??
Respondido acima.
Tipo uma string mesmo com 1 caracter tem tamanho dois, por que é o caracter + o 'barra0' delimitador do tamanho certo?
Mas o tipo char nao é apenas 1 byte??
Veja a explicação lá em cima sobre o uso conveniente de texto entre aspas.