Eduardo Bittencourt Posted August 19, 2017 Posted August 19, 2017 Ponteiros em C Olá, vou fazer um breve resumo do que eu aprendi até agora sobre ponteiros, lembrando que eu não fiz nenhuma formatação, e nem vou me aprofundar neste assunto, até mesmo porque tenho muito o que treinar ainda, se alguém quiser incrementar algo é só falar. 1->Todo ponteiro, pode aponta para algum lugar, e guardar o endereço de memória do dado apontado. O ponteiro tem o tamanho de um inteiro, e ao apontar para um dado, tem o tamanho deste dado. Vale ressaltar, que o tamanho pode variar de acordo o sistema operacional ou arquitetura. #include <stdio.h> int main(void) { char *ptr; printf("%d", sizeof(ptr)); //tamanho de um unsigned inteiro printf("%d", sizeof(*ptr)); //tamanho de um char, porque neste caso ptr está apontando para este tipo de dado return 0; } 2-Pode se dizer que todo array, faz referência a um ponteiro e vice versa. Internamente o compilador considera, que todo nome de um array é um ponteiro para o primeiro índice deste array, e seu tamanho é fixo e linear na memória, que é incrementado em bytes de acordo com o seu tipo. #include <stdio.h> #include <string.h> int main(void) { char nome[] = {'t','e','s','t','e','\0'}; int i; for(i=0; i < strlen(nome); i++) printf("Letra: %c Endereço de memória: %p\n", nome[i], nome[i]); // percorre todo o array e retorna o valor que está em cada posição, e o endereço de mem´´oria // note que só incrementa de 1 byte na memória de forma linear return 0; } 3-Um ponteiro pode apontar diretamente para um array, sem precisar usar o operador de referência &, isso ocorre porque todo nome de um array é um ponteiro, e a primeira posição tem o mesmo endereço de memória do nome da variável, obs: Só tem o mesmo endereço de memória para arrays, e não para ponteiros. ¨#include <stdio.h> int main(void) { char *ptr = "Ponteiro"; // Isso aqui é uma constante estática, o compilador coloca essa variável na seção .rodata, que apenas permite leitura, por isso é uma constante //Obviamente todo array começa no 1 indice, ou seja, para temos todos dados ou lista, como quiser chamar, é necessaria informar apenas o 1 indice printf("%x", &ptr); // Exibe o endereço de memória do ponteiro em hexadecimal printf("%p", ptr); //Exibe o endereço de memória para onde o ponteiro aponta //Nota: O correto seria usar a conversão de formato %p, e não %x. para obter o endereço de memória de um ponteiro. Porque independente de arquitetura vai mostrar de maneira adequada char string[] = "Isso é um array"; //Array de chars, o compilador já faz o trabalho de contar os caracteres, e colocar nullbyte no final printf("%x", &string); // Endereço de memória de string em hexadecimal printf("%p", string); // Endereço de memória para onde string aponta, justamente o mesmo endereço do nome do array ptr = string; // não preciso usar o operador ¨&, porque o nome string é um ponteiro para &string[0]; /*ptr é um atalho para &ptr[0], quando usamos a função printf, ou demais funções, o compilador está na verdade lendo o endereço de memória da variável, isso ocorre quando a váriavel é um array*/ printf("%s", &ptr[0]); return 0; } 4-Operadores: *->Dereferência ¨&->Referência Atalho->*ptr == ptr[0] ambos são a mesma coisa O operador & comercial, retorna o endereço de memória de uma variável, ou até mesma uma função O operador * asterisco, retorna o conteúdo para onde o ponteiro está apontando #include <stdio.h> #include <stdlib.h> /*A função func, nada mais é do que uma tentativa de explicar que não podemos modificar o valor que está no endereço da variável numero[1] Porque não apontamos para o endereço de memória, e sim colocamos o valor que está em numero[1], em x[0]*/ void func(void) { int *x = malloc(sizeof(int)); // aloco 4 bytes do tipo inteiro int numero[5] = {1,2,3,4,5}; *x = numero[1]; printf("Soma do valor do ptr: %d\n", *x+5); numero[1] = 10; printf("%d\n", *x); free(x); } int main(void) { int *x ; int numero[5] = {1,2,3,4,5}; x = &numero[1]; // recebe o endereço de memória de numero[1] , vou ter todo o conteúdo a partir do elemento numero[1] em diante printf("%d\n", *x); /*Ponteiro inicialmente sempre aponta para o começo de um dado, neste caso retorna o contéudo para onde o ponteiro está apontando */ printf("%d\n", *++x); // Incremento, e depois exibe o número 3 numero[1] = 10; printf("%d\n", *(x-1)); /*Estou decrementando 1 na posição, equivalente ao decremento de 4 bytes, não é recomendado usar valores negativos, porque pode trazer problemas de segurança e warnings na compilação.*/ func(); return 0; } 5-Ao criamos um ponteiro, sempre é bom inicializa-lo, pois o ponteiro pode apontar para um endereço de memória inválida ou sem permissão de leitura, causando lixo de memória, e encerrando com o sinal SIGSEGV, para isso não ocorrer, podemos inicializamos com um valor, especificamente 0, que limpa. #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int *ptr = 0; // Ponteiro aponta para 0, logo temos uma area de memória válida char *pointer = NULL; // Aponta para a constante null // As duas declarações fazem a mesma coisa, aponta para o 0 char *ptr; char nome[256]; memset(nome, 0, sizeof(nome)); / * Escreve na memória o valor 0, para a variável nome, simplificando, inicializei todos os dados da variável nome que é um array*/ } 6-A função memset escreve no endereço de memória um valor, serve para inicializar uma array, ponteiro ou estrutura, enfim. Por isso que é muito utilizado para limpar um buffer, porque pode simplesmente inicializar todos os elementos do array com 0. Parte corrigida graças ao Mercês, no programa abaixo utilizo a função memset pra escrever o byte x no endereço de memória, fiz isso para demonstrar que podemos usar a função memset pra escrever um byte em memória, por em não é a melhor forma, a não ser, se quisermos inicializar um array, nos comentários o Mercês explica a maneira correta #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { char *ptr = malloc(11); // Aloca 11 bytes, 10 bytes para a string, e 1 byte a mais para o nullbyte strcpy(ptr, "aaaaaaaaab"); char *aux = ptr; while (*ptr != 'b') { // percorre o loop até achar a letra b memset(ptr, 'x', sizeof(char)); // escreve o byte 'x' no endereço de memória apontado por ptr (a escrita rola sizeof(char) vezes - no caso aqui é 1) ptr++; // incrementa sizeof(char) bytes no endereço para o qual o ponteiro aponta (seu valor) } puts(aux); free(aux); return 0; } 7-Podemos manipular o endereço de memória de várias formas, uma dessas é quando passamos um argumento do tipo ponteiro na função. Na verdade estamos pegando o seu endereço de memória, e passando para uma função genérica, e com isso mudamos o que tem dentro do endereço de memória, essa função dá se o nome de passagem por referência. Obs: Ao utilizar-se uma função que pede como argumento um ponteiro, não nos obriga a passar uma variável que seja ponteiro, pois o que importa realmente é o endereço de memória, lembre-se que, o nome do ponteiro sempre aponta para o próximo elemento e pega o seu endereço de memória, logo assim, simplesmente podemos passar uma variável que não é ponteiro apenas informando o seu endereço de memória com o operador ¨¨& de referência. #include <stdio.h> #include <stdlib.h> char *string(char *ptr) { *ptr = 'b'; //colocar o b no endereço de memória do ptr return ptr; // retorna um ptr } int main(void) { char caracter = 'a'; printf("%c", caracter); string(&caracter); //pega o endereço de memória da variável caracter printf("%c", caracter); } 8-Utilizar-se ponteiros no código, pode garantir performance, e otimização com código genéricos, alocar memória dinamicamente, manipular endereços de memória, fazer callbacks, e um monte afim, além de ser mais visível achar erros. Segue um exemplo bem básico de ponteiro para função #include <stdio.h> #include <stdlib.h> int func(int a, int b) { return a + b; } int main(void) { int x = 5, y = 5; int (*ptr)(int x, int y); ptr = 0; printf("Ponteiro aponta para 0 : %p\n", ptr); ptr = func; printf("Ponteiro aponta para func: %p\nValor: %d", ptr, ptr(x,y)); } 9-O uso de ponteiros é bem extenso, no código abaixo vou deixar um exemplo de um array de ponteiros, de antemão, já digo que talvez não tenha explicado de maneira correta, pois simplifiquei, para ficar mais fácil o entendimento. Fiquei sem falar sobre alguns usos de ponteiros, tais como ponteiro para ponteiro, ponteiro para um array de um tipo de dado, entre outros, mas nestes casos, não vejo tanta usabilidade nos códigos. #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *ptr[] = {"Ponteiro", "is", "legal", NULL}; // Array de ponteiros puts(*ptr); // Exibe a string para onde o primeiro ponteiro aponta printf("%s\n%s\n", *(ptr+1), *(ptr+2)); // Exibe as strings avançado os bytes dos ponteiros putchar(ptr[0][5]); /* No primeiro [], informo q meu primeiro ponteiro aponta para a String Ponteiros, e ao usar [] novamente, informo para avançar 5 bytes da onde o primeiro ponteiro aponta, o resultado é a letra i*/ putchar('\n'); // Um array bidimensional é diferente de um array de ponteiros, internamente eles se comportam de maneira diferente na memória return 0; } Obs: Quando falam em performance ao usar ponteiros, quer dizer na verdade que são mais velozes, devido ao código que o compilador gcc gera, por isso logo vai ter menos ciclos e o processador vai executar mais rápido o programa. Bom, vou ficando por aqui, isso foi um resumo bem simples sobre ponteiros, fiquei na dúvida se postava ou não, porque sou bem noob em C, mas acredito que o fórum seja pra isso mesmo, compartilhar, independente do nível de conhecimento de cada um, espero que um dia eu fique bom nesse treco, haha. Agradecimentos ao Frederico e ao Mercês que me explicou sobre uso de ponteiros, lembrando, se tiver algum erro me avisem, ou se ficou ruim o texto desse jeito, seguido de código e mais código. Bye bye ao som de Vanessa Carlton - A thousand Miles
hanczjeeel Posted August 29, 2017 Posted August 29, 2017 Muito boa sua iniciativa, parabéns! fiquei com uma dúvida no tópico 8 na seguinte linha: int(*ptr)(int x, int y); qual seria a lógica desta linha?
Eduardo Bittencourt Posted August 29, 2017 Author Posted August 29, 2017 2 horas atrás, hanczjeeel disse: Muito boa sua iniciativa, parabéns! fiquei com uma dúvida no tópico 8 na seguinte linha: int(*ptr)(int x, int y); qual seria a lógica desta linha? É um ponteiro para função, int quer dizer que o ponteiro vai retornar um inteiro, *ptr estou declarando o ponteiro, (int x. int y), informo que a função tem que passar dois parâmetros inteiros, entendeu?
hanczjeeel Posted August 29, 2017 Posted August 29, 2017 1 hora atrás, Eduardo Bittencourt disse: É um ponteiro para função, int quer dizer que o ponteiro vai retornar um inteiro, *ptr estou declarando o ponteiro, (int x. int y), informo que a função tem que passar dois parâmetros inteiros, entendeu? Entendi, obrigado. fiz uns testes aqui, por exemplo ali, pode-se chamar a função func por: int soma1 = func(1,3); ou int soma2 = ptr(1,4); bem louco.
Eduardo Bittencourt Posted August 29, 2017 Author Posted August 29, 2017 2 minutos atrás, hanczjeeel disse: Entendi, obrigado. fiz uns testes aqui, por exemplo ali, pode-se chamar a função func por: int soma1 = func(1,3); ou int soma2 = ptr(1,4); bem louco. Isso ae, kkkk
Administrators Fernando Mercês Posted August 29, 2017 Administrators Posted August 29, 2017 Que legal, @Eduardo Bittencourt! O fórum é pra isso mesmo! Acho fantástico quando alguém perde o medo e posta! haha Olha, eu não sou especialista no assunto mas, a meu ver, tem coisas que podem ser elevadas de nível, exatamente como o Frederico fez com as minhas aulas no canal. Mas são detalhes, tipo quando você fala que o ponteiro aponta pro endereço de uma variável, mas na verdade pode apontar pra qualquer lugar. Claro, normalmente tem um dado lá. rsrs Mas enfim, como falei, são pequenos detalhes que a revisão do Frederico me ajudou a ficar mais esperto sobre! Como você pediu feedback, vou listar aqui: Ponteiros são variáveis que contém um endereço de memória. É melhor usar o %p ao imprimir endereços de memória, porque ele já adapta pra arquitetura (%#x ou %#lx). Olha a confusão (em 64-bits): $ echo 'main(){ int x; printf("%x\n%p\n", &x, &x); }' | tcc -run - 4fce29ac 0x7fff4fce29ac Pro size_t, usa o %zu por conta do tamanho (o Frederico fala sobre isso nos comentários no canal) A memset() é pra escrever em memória. No código do item 6, aquele free() não vai rolar porque você incrementou o endereço do ponteiro. Uma saída pra isso seria usando um ponteiro auxiliar, pra guardar o primeiro endereço antes de incrementar (já que você usou isso pra iterar por ele): int main(void) { char *ptr = malloc(11); // Aloca 11 bytes, 10 bytes para a string, e 1 byte a mais para o nullbyte strcpy(ptr, "aaaaaaaaab"); char *aux = ptr; while (*ptr != 'b') { // percorre o loop até achar a letra b memset(ptr, 'x', sizeof(char)); // escreve o byte 'x' no endereço de memória apontado por ptr (a escrita rola sizeof(char) vezes - no caso aqui é 1) ptr++; // incrementa sizeof(char) bytes no endereço para o qual o ponteiro aponta (seu valor) } puts(aux); free(aux); return 0; } Ou fazer o loop com for. Mas no caso dessa função (colocar 'x' até encontrar 'b'), como é um caractere só, não precisa da memset(): while (*ptr != 'b') { *ptr = 'x'; // coloca o 'x' no endereço de memória apontado por ptr ptr++; // incrementa ptr, então ele vai agora apontar para a próxima letra } Dá pra fazer tudo numa linha só também, graças à magia negra precedência de operadores: while (*ptr != 'b') *ptr++ = 'x'; Mas de novo, tá muito bom o texto. O assunto parece complicado e assusta a galera, mas você tratou de boa. Isso é muito maneiro! Grande abraço!
Eduardo Bittencourt Posted August 30, 2017 Author Posted August 30, 2017 1 hora atrás, Fernando Mercês disse: Que legal, @Eduardo Bittencourt! O fórum é pra isso mesmo! Acho fantástico quando alguém perde o medo e posta! haha Olha, eu não sou especialista no assunto mas, a meu ver, tem coisas que podem ser elevadas de nível, exatamente como o Frederico fez com as minhas aulas no canal. Mas são detalhes, tipo quando você fala que o ponteiro aponta pro endereço de uma variável, mas na verdade pode apontar pra qualquer lugar. Claro, normalmente tem um dado lá. rsrs Mas enfim, como falei, são pequenos detalhes que a revisão do Frederico me ajudou a ficar mais esperto sobre! Como você pediu feedback, vou listar aqui: Ponteiros são variáveis que contém um endereço de memória. É melhor usar o %p ao imprimir endereços de memória, porque ele já adapta pra arquitetura (%#x ou %#lx). Olha a confusão (em 64-bits): $ echo 'main(){ int x; printf("%x\n%p\n", &x, &x); }' | tcc -run - 4fce29ac 0x7fff4fce29ac Pro size_t, usa o %zu por conta do tamanho (o Frederico fala sobre isso nos comentários no canal) A memset() é pra escrever em memória. No código do item 6, aquele free() não vai rolar porque você incrementou o endereço do ponteiro. Uma saída pra isso seria usando um ponteiro auxiliar, pra guardar o primeiro endereço antes de incrementar (já que você usou isso pra iterar por ele): int main(void) { char *ptr = malloc(11); // Aloca 11 bytes, 10 bytes para a string, e 1 byte a mais para o nullbyte strcpy(ptr, "aaaaaaaaab"); char *aux = ptr; while (*ptr != 'b') { // percorre o loop até achar a letra b memset(ptr, 'x', sizeof(char)); // escreve o byte 'x' no endereço de memória apontado por ptr (a escrita rola sizeof(char) vezes - no caso aqui é 1) ptr++; // incrementa sizeof(char) bytes no endereço para o qual o ponteiro aponta (seu valor) } puts(aux); free(aux); return 0; } Ou fazer o loop com for. Mas no caso dessa função (colocar 'x' até encontrar 'b'), como é um caractere só, não precisa da memset(): while (*ptr != 'b') { *ptr = 'x'; // coloca o 'x' no endereço de memória apontado por ptr ptr++; // incrementa ptr, então ele vai agora apontar para a próxima letra } Dá pra fazer tudo numa linha só também, graças à magia negra precedência de operadores: while (*ptr != 'b') *ptr++ = 'x'; Mas de novo, tá muito bom o texto. O assunto parece complicado e assusta a galera, mas você tratou de boa. Isso é muito maneiro! Grande abraço! Vlw pelo feedback, ah propósito da função que uso while, não me toquei que aquela forma estava errada, fiz o teste com o valgrind e deu erro invalid free, enfim. Ah respeito de não usar o %p, na verdade estava até ciente, porém não coloquei porque podia confundir pra quem estava tendo o primeiro contato, mas pensando bem compensava muito ter colocado,era a melhor forma. Agradeço novamente pelo feedback e pelas suas correções, foram de grande ajuda, abraços
Administrators Fernando Mercês Posted August 30, 2017 Administrators Posted August 30, 2017 É, reconheço que certas decisões são difíceis ao escrever, porque realmente pode confundir. Entendo perfeitamente. Foi assim que fiz o curso também, e as sugestões de melhorias ficaram nos comentários do vídeo, como estão ficando aqui nos comentários do seu tópico. :-) Valeu!
Eduardo Bittencourt Posted August 30, 2017 Author Posted August 30, 2017 Incrementei mais algumas coisas. Peço, novamente que se errei algo, me corrijam
Administrators Fernando Mercês Posted October 4, 2017 Administrators Posted October 4, 2017 Pra mim tá show. Parabéns mesmo!
thiago Posted October 28, 2017 Posted October 28, 2017 E aí, cara? Gostei muito do que escreveu. Parabéns pela iniciativa de passar um assunto bem chato de maneira divertida. Se me permite, gostaria de te dar uns toques sobre o que você escreveu. Em 19/08/2017 em 10:58, Eduardo Bittencourt disse: 1->Todo ponteiro, pode aponta para algum lugar, e guardar o endereço de memória do dado apontado. O ponteiro tem o tamanho de um int, e ao apontar para um dado, tem o tamanho deste dado. Vale ressaltar, que o tamanho pode variar de acordo o sistema operacional ou arquitetura. Cuidado com a diferença sutil entre endereço de memória e conteúdo da memória naquele endereço. O ponteiro não tem o tamanho de um int; na verdade, o ponteiro tem o tamanho de uma palavra (normalmente está relacionada ao tamanho do registrador). Na arquitetura x86, o tamanho da palavra é 32 bits e o tamanho de um int também, porém isso não é verdade para a arquitetura x86_64. O tamanho da palavra é 64 bits e o tamanho de um int é 32 bits. Cuidado também com esse lance de "ao apontar para um dado, o tamanho do ponteiro terá o mesmo tamanho do dado". O tamanho de um ponteiro é o mesmo, esteja ele apontando para um char, short, int, long, etc. Em todos os casos, o ponteiro terá o tamanho da palavra. #include <stdio.h> int main(void) { printf("%zu\n", sizeof(char *)); printf("%zu\n", sizeof(short *)); printf("%zu\n", sizeof(int *)); printf("%zu\n", sizeof(long *)); printf("%zu\n", sizeof(long long *)); return 0; } Se você compilar e executar esse código no x86_64, vai ver que todos os resultados vão ser 8, mas se você executar no x86, os resultados vão ser 4. Aí você pode estar se perguntando sobre a relevância dos tipos nos ponteiros. Pra responder isso, ser liga nesse trecho de código: int main(void) { char num1; short num2; int num3; void *ptr = &num1; *(char *) ptr = 100; ptr = &num2; *(short *) ptr = 100; ptr = &num3; *(int *) ptr = 100; return 0; } Agora se liga no código Assembly correspondente: main: push ebp mov ebp, esp sub esp, 16 lea eax, [ebp-5] mov DWORD PTR [ebp-4], eax mov eax, DWORD PTR [ebp-4] mov BYTE PTR [eax], 100 lea eax, [ebp-8] mov DWORD PTR [ebp-4], eax mov eax, DWORD PTR [ebp-4] mov WORD PTR [eax], 100 lea eax, [ebp-12] mov DWORD PTR [ebp-4], eax mov eax, DWORD PTR [ebp-4] mov DWORD PTR [eax], 100 mov eax, 0 leave ret Tá vendo esses "mov BYTE PTR [eax], 100", "mov WORD PTR [eax], 100" e "mov DWORD PTR [eax], 100"? Tá ligado nesses prefixos BYTE, WORD e DWORD? Um char tem o tamanho de 1 byte (BYTE), um short tem o tamaho de 2 bytes (ou uma WORD) e um int tem o tamanho de 4 bytes (ou uma DWORD). A questão do tipo só serve pra informar pra o compilador a quantidade de memória em bytes que será carregada em um registrador ou a quantidade de bytes de um registrador que será armazenada em um determinado endereço de memória. Observe que na parte onde eu faço a atribuição dos endereços de memória para a variável ptr, os prefixos são DWORD (veja todos os mov DWORD PTR [ebp-4], eax). Isso confirma o que eu te falei sobre o tamanho do ponteiro ser o mesmo para qualquer tipo. No caso, o tamanho do ponteiro é DWORD, logo podemos concluir que o tamanho da palavra é 32 bits. Espero que eu não tenha complicado demais. Abraços!
Eduardo Bittencourt Posted October 28, 2017 Author Posted October 28, 2017 7 horas atrás, thiago disse: E aí, cara? Gostei muito do que escreveu. Parabéns pela iniciativa de passar um assunto bem chato de maneira divertida. Se me permite, gostaria de te dar uns toques sobre o que você escreveu. Cuidado com a diferença sutil entre endereço de memória e conteúdo da memória naquele endereço. O ponteiro não tem o tamanho de um int; na verdade, o ponteiro tem o tamanho de uma palavra (normalmente está relacionada ao tamanho do registrador). Na arquitetura x86, o tamanho da palavra é 32 bits e o tamanho de um int também, porém isso não é verdade para a arquitetura x86_64. O tamanho da palavra é 64 bits e o tamanho de um int é 32 bits. Cuidado também com esse lance de "ao apontar para um dado, o tamanho do ponteiro terá o mesmo tamanho do dado". O tamanho de um ponteiro é o mesmo, esteja ele apontando para um char, short, int, long, etc. Em todos os casos, o ponteiro terá o tamanho da palavra. Creio eu, que um endereço de memória de um ponteiro tem tamanho de um inteiro, no qual o tamanho pode ser variado. Mas não entendi, o que você quis dizer com tamanho de uma palavra, como assim palavra? E a respeito sobre apontar para char, short, int, long, etc, o tamanho pode variar sim, quando você aponta para um char, você estará reservando apenas 1 byte para aquele endereço de memória. Exemplo: char *teste = "array"; printf("%d", sizeof(*teste)); // Tamanho de 1 byte Ah respeito do código em assembly, eu entendi o que você quis dizer, mt obrigado pelo feedback
thiago Posted October 28, 2017 Posted October 28, 2017 1 hora atrás, Eduardo Bittencourt disse: Creio eu, que um endereço de memória de um ponteiro tem tamanho de um inteiro, no qual o tamanho pode ser variado. Mas não entendi, o que você quis dizer com tamanho de uma palavra, como assim palavra? E a respeito sobre apontar para char, short, int, long, etc, o tamanho pode variar sim, quando você aponta para um char, você estará reservando apenas 1 byte para aquele endereço de memória. Exemplo: char *teste = "array"; printf("%d", sizeof(*teste)); // Tamanho de 1 byte Ah respeito do código em assembly, eu entendi o que você quis dizer, mt obrigado pelo feedback Não entendi qual foi sua dúvida sobre a palavra. A dúvida é sobre o conceito de palavra? Eu rodei seu código aqui na minha máquina, modifiquei e aproveitei pra montar este esquema pra simplificar. O tamanho do ponteiro (tamanho do endereço) é o mesmo pra qualquer que seja o tipo (char, short, int, long, etc.). Você simplesmente obteve o tamanho de *teste, que é o mesmo tamanho de teste[0], que é o mesmo tamanho de um char, que possui o tamanho de 1 byte. Se ligue que são duas coisas diferentes: tamanho do endereço é uma coisa e tamanho do conteúdo apontado por aquele endereço é outra. Abraços!
Eduardo Bittencourt Posted October 28, 2017 Author Posted October 28, 2017 5 horas atrás, thiago disse: Não entendi qual foi sua dúvida sobre a palavra. A dúvida é sobre o conceito de palavra? Eu rodei seu código aqui na minha máquina, modifiquei e aproveitei pra montar este esquema pra simplificar. O tamanho do ponteiro (tamanho do endereço) é o mesmo pra qualquer que seja o tipo (char, short, int, long, etc.). Você simplesmente obteve o tamanho de *teste, que é o mesmo tamanho de teste[0], que é o mesmo tamanho de um char, que possui o tamanho de 1 byte. Se ligue que são duas coisas diferentes: tamanho do endereço é uma coisa e tamanho do conteúdo apontado por aquele endereço é outra. Abraços! É isso que estou dizendo, o endereço de memória de um ponteiro sempre vai ter o tamanho de um inteiro. Mas quando você aponta para outro dado, o endereço do dado vai ter o tamanho do tipo apontado, abraços
Administrators Fernando Mercês Posted October 29, 2017 Administrators Posted October 29, 2017 Fala galera, Uma maneira de lembrar é pensar no processador. O tamanho da palavra é o tamanho de um endereço de memória. Num programa de 32-bits um endereço tem 32-bits. Num de 64 tem 64-bits e assim vai. Ou imprimir sizeof(void*) pra tirar a dúvida. Abraços!
thiago Posted October 29, 2017 Posted October 29, 2017 3 horas atrás, Fernando Mercês disse: Fala galera, Uma maneira de lembrar é pensar no processador. O tamanho da palavra é o tamanho de um endereço de memória. Num programa de 32-bits um endereço tem 32-bits. Num de 64 tem 64-bits e assim vai. Ou imprimir sizeof(void*) pra tirar a dúvida. Abraços! Excelente dica, cara.
thiago Posted October 29, 2017 Posted October 29, 2017 20 horas atrás, Eduardo Bittencourt disse: É isso que estou dizendo, o endereço de memória de um ponteiro sempre vai ter o tamanho de um inteiro. Mas quando você aponta para outro dado, o endereço do dado vai ter o tamanho do tipo apontado, abraços Acho que a gente não tá conseguindo se entender, cara. Por exemplo, a sua definição de que o tamanho do endereço vai ter o tamanho de um inteiro é inválida no caso do x86_64. Como o @Fernando Mercês disse, o tamanho de um endereço numa arquitetura de 64 bits é 64 bits. Compila e testa isso (no x86_64): #include <stdio.h> int main() { printf("%zu %zu\n", sizeof(void *), sizeof(int)); return 0; } Você vai ver que sizeof(void *) é 8 e sizeof(int) é 4. Acho que você está confundindo um pouco as coisas. O que você está dizendo sobre o endereço do dado ter o tamanho do tipo apontado não é verdade. Numa arquitetura de 64 bits, se um ponteiro aponta pra um char, o tamanho do ponteiro é 64 bits; se ele aponta pra um short, int, long, float, double, o tamanho do ponteiro continua sendo 64 bits. A melhor maneira de ver isso é testando no próprio código.
Eduardo Bittencourt Posted October 30, 2017 Author Posted October 30, 2017 14 horas atrás, thiago disse: Acho que a gente não tá conseguindo se entender, cara. Por exemplo, a sua definição de que o tamanho do endereço vai ter o tamanho de um inteiro é inválida no caso do x86_64. Como o @Fernando Mercês disse, o tamanho de um endereço numa arquitetura de 64 bits é 64 bits. Compila e testa isso (no x86_64): #include <stdio.h> int main() { printf("%zu %zu\n", sizeof(void *), sizeof(int)); return 0; } Você vai ver que sizeof(void *) é 8 e sizeof(int) é 4. Acho que você está confundindo um pouco as coisas. O que você está dizendo sobre o endereço do dado ter o tamanho do tipo apontado não é verdade. Numa arquitetura de 64 bits, se um ponteiro aponta pra um char, o tamanho do ponteiro é 64 bits; se ele aponta pra um short, int, long, float, double, o tamanho do ponteiro continua sendo 64 bits. A melhor maneira de ver isso é testando no próprio código. Acho que na verdade está havendo uma falha de comunicação entre nós. Quando digo inteiro, não quero dizer que o ponteiro tem o tamanho do tipo int, mas sim que o tamanho não é um ponto flutuante, entendeu? Eu sei que em arquiteturas diferentes, tem tamanho distintos. Um ponteiro em arquitetura x86_x64 tem tamanho de um inteiro com modificador de tamanho long long 8 bytes, já em ambientes i386 o ponteiro geralmente tem tamanho de 4 bytes, ambos são inteiros, concorda comigo? Ao utilizar sizeof(*ptr); Está retornando o tamanho do conteúdo para onde o ponteiro está apontando ptr, abraços.
thiago Posted October 30, 2017 Posted October 30, 2017 8 horas atrás, Eduardo Bittencourt disse: Acho que na verdade está havendo uma falha de comunicação entre nós. Quando digo inteiro, não quero dizer que o ponteiro tem o tamanho do tipo int, mas sim que o tamanho não é um ponto flutuante, entendeu? Eu sei que em arquiteturas diferentes, tem tamanho distintos. Um ponteiro em arquitetura x86_x64 tem tamanho de um inteiro com modificador de tamanho long long 8 bytes, já em ambientes i386 o ponteiro geralmente tem tamanho de 4 bytes, ambos são inteiros, concorda comigo? Huahuahuahauhauhauha! Nossa, cara! Que confusão! Mas isso é natural, estamos falando de ponteiros, né? Sim, sim, esse lance de falar que não é ponto flutuante me confundiu todo. Em x86_64, um ponteiro tem o tamanho de um long ou long long (no x86_64) e long long (no x86). Abração!
Eduardo Bittencourt Posted October 30, 2017 Author Posted October 30, 2017 É normal isso, kkkk. Fui perceber depois, que você estava entendo inteiro como int, o que é normal, por isso não estávamos nos entendendo. Abraços amigo!
Rick Santos Posted November 11, 2017 Posted November 11, 2017 Só para acrescentar um ponto relevante relacionado à aritmética de ponteiros: Ao nos referenciar-mo-nos a ptr[3] estamos a referir-nos ao endereço de ponteiro como base mais um offset(deslocamento) entre [], quando falamos em endereços, para obter-mos o endereço correto de uma referência como a de cima teremos de multiplicar o offset pelo sizeof(tipo_de_ponteiro) que está a ser usado. Exemplo: ptr[3] é igual a *(ptr + 3), o que na realidade vai fazer referência a *(ptr + 3 * sizeof(ptr)), como referi acima, se ptr apontar por exemplo para 0x11 e usarmos ptr[3] na realidade vamos nos referir ao endereço 0x1d, pois foi somado a ponteiro 3 * sizeof(ptr). Um outro detalhe interessante, é o uso da propriedade comutativa na aritmética de ponteiros... Exemplo: Ter ptr[3] ou 3[ptr] é exatamente a mesma coisa, pois ptr[3] significa em termos de programação *(ptr + 3) e 3[ptr] significa *(3 + ptr), e seguindo a propriedade comutativa da matemática a + b = b + a, querendo assim dizer que ptr + 3 <=> 3 + ptr. Resumindo ptr[3] e 3[ptr] são iguais, a unica consideração a se levar é que por vezes o compilador pode gerar um aviso em relação à segunda durante o processo de compilação. Por fim, este uso esquisito de aritmética de ponteiros também pode ser usado com arrays, considerado que o símbolo pelo qual a array é representado, por exemplo "a", onde "a" é igual a "&a[0]" (ou seja "a" é um ponteiro para o endereço de memória do primeiro "item" do array), assim qualquer deslocamento numa array, por exemplo a[5], pode também ser representado por *(a + 5) ou *(5 + a) devido à propriedade de comutativa que demonstrei acima. Foi apenas um acréscimo ao tópico. ### SEM QUERER MULTICITEI O CONTEÚDO ### Agora, Rick Santos disse: Só para acrescentar um ponto relevante relacionado à aritmética de ponteiros: Ao nos referenciar-mo-nos a ptr[3] estamos a referir-nos ao endereço de ponteiro como base mais um offset(deslocamento) entre [], quando falamos em endereços, para obter-mos o endereço correto de uma referência como a de cima teremos de multiplicar o offset pelo sizeof(tipo_de_ponteiro) que está a ser usado. Exemplo: ptr[3] é igual a *(ptr + 3), o que na realidade vai fazer referência a *(ptr + 3 * sizeof(ptr)), como referi acima, se ptr apontar por exemplo para 0x11 e usarmos ptr[3] na realidade vamos nos referir ao endereço 0x1d, pois foi somado a ponteiro 3 * sizeof(ptr). Um outro detalhe interessante, é o uso da propriedade comutativa na aritmética de ponteiros... Exemplo: Ter ptr[3] ou 3[ptr] é exatamente a mesma coisa, pois ptr[3] significa em termos de programação *(ptr + 3) e 3[ptr] significa *(3 + ptr), e seguindo a propriedade comutativa da matemática a + b = b + a, querendo assim dizer que ptr + 3 <=> 3 + ptr. Resumindo ptr[3] e 3[ptr] são iguais, a unica consideração a se levar é que por vezes o compilador pode gerar um aviso em relação à segunda durante o processo de compilação. Por fim, este uso esquisito de aritmética de ponteiros também pode ser usado com arrays, considerado que o símbolo pelo qual a array é representado, por exemplo "a", onde "a" é igual a "&a[0]" (ou seja "a" é um ponteiro para o endereço de memória do primeiro "item" do array), assim qualquer deslocamento numa array, por exemplo a[5], pode também ser representado por *(a + 5) ou *(5 + a) devido à propriedade de comutativa que demonstrei acima. Foi apenas um acréscimo ao tópico.
Eduardo Bittencourt Posted November 11, 2017 Author Posted November 11, 2017 Vlw pelo acréscimo. Ficou muito bom a sua complementação.
Administrators Fernando Mercês Posted November 14, 2017 Administrators Posted November 14, 2017 Sim, belo acréscimo. Acho que temos uma excelente referência sobre ponteiros no portal já.
Recommended Posts
Archived
This topic is now archived and is closed to further replies.