Baixe o app para aproveitar ainda mais
Prévia do material em texto
Tudo sobre ponteiros 0) Tabela de bases 1) Memória 2) Tipos de variáveis 3) Tipos-ponteiro 4) Aritmética 5) Tópicos interessantes Tudo sobre ponteiros 0) Tabela de bases Tabela de bases Decimal Binário Hexadecimal 00 0000 0 01 0001 1 02 0010 2 03 0011 3 04 0100 4 05 0101 5 06 0110 6 07 0111 7 Decimal Binário Hexadecimal 08 1000 8 09 1001 9 10 1010 A 11 1011 B 12 1100 C 13 1101 D 14 1110 E 15 1111 F Tudo sobre ponteiros 1) Memória O que é memória? ... ... ... O que é memória? ... ... ... 1 byte 0 0 0 1 0 1 0 0 8 bits O que é memória? ... ... ... 0xFF00 0xFF01 0xFF02 0xFF03 0xFF89 0xFF8A 1 byte Endereços! 0 0 0 1 0 1 0 0 8 bits Qual o tamanho da memória? ... ... ... 0xFF00 0xFF01 0xFF02 0xFF03 0xFF89 0xFF8A 1 byte 0 0 0 1 0 1 0 0 8 bits Endereços! Qual o tamanho da memória? ... ... ... 0xFF00 0xFF01 0xFF02 0xFF03 0xFF89 0xFF8A 1 byte 0 0 0 1 0 1 0 0 8 bits Endereços! Qual o tamanho da memória? ... ... ... 0xFF00 0xFF01 0xFF02 0xFF03 0xFF89 0xFF8A 1 byte 0 0 0 1 0 1 0 0 8 bits Endereços! Qual o tamanho da memória? 0xFF00 Qual o tamanho da memória? 0xFF00 Cada dígito engloba 4 bits. Logo, o número possui 16 bits 0x indica que o número está na base hexadecimal Qual o tamanho da memória? 0xFF00 Cada dígito engloba 4 bits. Logo, o número possui 16 bits 0x indica que o número está na base hexadecimal Portanto, ESTA memória possui até 216 bytes = 64 KiB! Tamanho da memória Quem determina o tamanho da memória? Tamanho da memória Quem determina o tamanho da memória? O tipo do processador! Tamanho da memória Processador x86: faz endereçamento de memória usando 32 bits! Portanto: 1) Endereços com 32 bits: 0xFFA8004C 2) Tamanho máximo: 232 bytes = 4 GiB Tamanho da memória Processador x64: faz endereçamento de memória usando 48 bits! Portanto: 1) Endereços com 48 bits: 0xFFA8004CBBDD 2) Tamanho máximo: 248 bytes = 256 TiB Como usamos a memória? Basicamente, lendo e escrevendo bytes nela, através das variáveis dos nossos programas. Tudo sobre ponteiros 2) Tipos de variáveis O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 Do ponto de vista da máquina, na memória estão apenas bits! O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 Do ponto de vista da máquina, na memória estão apenas bits! Mas do ponto de vista do programador, o que está na memória depende... O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 Mas do ponto de vista do programador, o que está na memória depende… ...do tipo de variável associado ao enderenço que nos referimos. O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos este byte no endereço 0xFF00 como um char, ele será avaliado como '\n' O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos este byte no endereço 0xFF0B como um char, ele será avaliado como '"' (aspas duplas) O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos este byte no endereço 0xFF0C como um char, ele será avaliado como 'F' O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF01 como um int, ela será avaliada como 1047548 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int, ela será avaliada como -52428800 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um unsigned int, ela será avaliada como 4242538496 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF09 como um float, ela será avaliada como -3.6893488E19 O que está na memória? 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF05 como um double, ela será avaliada como - 2.6815618826769465E154 Conclusão? Cada tipo de variável nos fornece três coisas. Até agora, vimos duas: 1) Espaço ocupado na memória 2) Como interpretar o padrão de bits Conclusão? Cada tipo de variável nos fornece três coisas. Até agora, vimos duas: 1) Espaço ocupado na memória 2) Como interpretar o padrão de bits Observação: O conjunto do compilador, com o sistema operacional e o processador, o que podemos chamar de plataforma, é o que determina o espaço ocupado por cada tipo. Conclusão? Cada tipo de variável nos fornece três coisas. Até agora, vimos duas: 1) Espaço ocupado na memória 2) Como interpretar o padrão de bits Observação: O conjunto do compilador, com o sistema operacional e o processador, o que podemos chamar de plataforma, é o que determina o espaço ocupado por cada tipo. A terceira coisa, que veremos adiante, são as operações aritméticas. Tudo sobre ponteiros 3) Tipo-ponteiro Tipo ponteiro Até agora, vimos que os tipos determinam o espaço alocado na memória para cada variável além de como interpretar a sequência de bits que está armazenada. Tipo ponteiro Até agora, vimos que os tipos determinam o espaço alocado na memória para cada variável além de como interpretar a sequência de bits que está armazenada. Além disso, vimos também os tipos char, int, unsigned int, float e double. Os três primeiros são ditos tipos integrais e os dois últimos são ditos tipos reais. Tipo ponteiro Até agora, vimos que os tipos determinam o espaço alocado na memória para cada variável além de como interpretar a sequência de bits que está armazenada. Além disso, vimos também os tipos char, int, unsigned int, float e double. Os três primeiros são ditos tipos integrais e os dois últimos são ditos tipos reais. O que estes cinco tipos têm em comum, é o fato de que eles armazenam dados dos nossos programas. Tipo ponteiro Existem tipos para armazenar endereços de variáveis dos nossos programas! Tipo ponteiro Existem tipos para armazenar endereços de variáveis dos nossos programas! São o que chamaremos de tipos-ponteiro, já que eles apontam para um espaço de memória. Tipo ponteiro Existem tipos para armazenar endereços de variáveis dos nossos programas! São o que chamaremos de tipos-ponteiro, já que eles apontam para um espaço de memória. Tipos-ponteiro também se encaixam nos tipos integrais, pois existe uma quantidade inteira de posições na memória. Exemplos 0A 00 0F FB FC ... ... E0 00 00 001D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF03 como um char*, ela será avaliada como 0xFBFC Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um char*, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int*, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF03 como um int*, ela será avaliada como 0xFBFC Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF03 como um double*, ela será avaliada como 0xFBFC Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um double*, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um void*, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF03 como um void*, ela será avaliada como 0xFBFC Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF03 como um void*, ela será avaliada como 0xFBFC Espera! O que...?! void* Espera! O que...?! void* void é um tipo especial. Não é possível criar variáveis do tipo void, mas é possível criar ponteiros para void. A utilidade é armazenar endereços de variáveis de qualquer tipo. Espera! O que...?! void* void é um tipo especial. Não é possível criar variáveis do tipo void, mas é possível criar ponteiros para void. A utilidade é armazenar endereços de variáveis de qualquer tipo. Isto quer dizer que uma variável do tipo void* pode armazenar tanto o endereço de uma variável do tipo int, quanto o endereço de uma variável do tipo double. Conclusões? Duas! Conclusões? Duas! 1) O espaço ocupado por um ponteiro de qualquer tipo é sempre o mesmo! Qual espaço? Aquele que o processador define! x86: 4 bytes; x64: 8 bytes! Conclusões? Duas! 1) O espaço ocupado por um ponteiro de qualquer tipo é sempre o mesmo! Qual espaço? Aquele que o processador define! x86: 4 bytes; x64: 8 bytes! 2) A interpretação de um ponteiro de qualquer tipo é sempre a mesma! Qual é? Um endereço de memória, que costumamos expressar em hexadecimal! Conclusões? Isso é tudo? Conclusões? Isso é tudo? Não Conclusões? Isso é tudo? Não Existem tipos-ponteiro para qualquer tipo de variável. Inclusive para tipo-ponteiro. Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int**, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int***, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int****, ela será avaliada como 0xFCE0 Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Se interpretarmos esta sequência de bytes a partir do endereço 0xFF04 como um int*****, ela será avaliada como 0xFCE0 Ok. Mas como usar? A pergunta que não quer calar é: Ok! Posso criar variáveis que armazenam o endereço de outras variáveis. E como eu vou saber qual é o endereço de uma variável, para que eu possa armazená-lo? Ok. Mas como usar? A pergunta que não quer calar é: Ok! Posso criar variáveis que armazenam o endereço de outras variáveis. E como eu vou saber qual é o endereço de uma variável, para que eu possa armazená-lo? Com o operador &. A linguagem C nos dá este operador que retorna o endereço de uma variável. Exemplos 0A 00 0F FB FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 Exemplos 00 00 00 32 FC ... ... E0 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; Exemplos 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Exemplos 00 00 00 32 FF ... ... 00 FF 04 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 FF 00 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; ptr = &i; Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 FF 04 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; ptr = &i; ptr = π Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 FF 06 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; ptr = &i; ptr = π ptr = &ppi; Exemplos 00 00 00 32 FF ... ... 00 FF 04 FF 06 FF 08 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; ptr = &i; ptr = π ptr = &ppi; ptr = &pppi; O que faço com um endereço? 00 00 00 32 FF ... ... 00 FF 04 FF 06 FF 08 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; void* ptr; ptr = &i; ptr = π ptr = &ppi; ptr = &pppi; O que faço com um endereço? Ok. Já sei armazenar os endereços das minhas variáveis. E o que eu faço com eles? O que faço com um endereço? Ok. Já sei armazenar os endereços das minhas variáveis. E o que eu faço com eles? Conhecendo o endereço de uma variável, é possível conhecer o seu valor sem usar seu nome diretamente! O que faço com um endereço? Ok. Já sei armazenar os endereços das minhas variáveis. E o que eu faço com eles? Conhecendo o endereço de uma variável, é possível conhecer o seu valor sem usar seu nome diretamente! E quando precisarei conhecer o valor de umavariável sem usar seu nome diretamente? O que faço com um endereço? Ok. Já sei armazenar os endereços das minhas variáveis. E o que eu faço com eles? Conhecendo o endereço de uma variável, é possível conhecer o seu valor sem usar seu nome diretamente! E quando precisarei conhecer o valor de uma variável sem usar seu nome diretamente? Quando eu conhecer não o nome, mas apenas o endereço de uma variável. Ou seja, em funções! Desreferenciação Através do operador *, fazemos o que chamamos de desreferenciação. Desreferenciação Através do operador *, fazemos o que chamamos de desreferenciação. A operação de desreferenciar é auto-explicativa. Desreferenciação Através do operador *, fazemos o que chamamos de desreferenciação. A operação de desreferenciar é auto-explicativa. Quando possuímos um ponteiro para uma variável, costumamos dizer que possuímos uma referência para esta variável. Desreferenciação Através do operador *, fazemos o que chamamos de desreferenciação. A operação de desreferenciar é auto-explicativa. Quando possuímos um ponteiro para uma variável, costumamos dizer que possuímos uma referência para esta variável. Se desreferenciamos um ponteiro, tiramos a referência e passamos a lidar com a variável propriamente dita. O que isto quer dizer? Quando mandamos imprimir o valor de um ponteiro, o que vemos é um número escrito na base hexadecimal. O que isto quer dizer? Quando mandamos imprimir o valor de um ponteiro, o que vemos é um número escrito na base hexadecimal. Ou seja, quando imprimimos o valor de um ponteiro, imprimimos o que ele significa: um endereço de memória. O que isto quer dizer? Quando mandamos imprimir o valor de um ponteiro, o que vemos é um número escrito na base hexadecimal. Ou seja, quando imprimimos o valor de um ponteiro, imprimimos o que ele significa: um endereço de memória. Quando mandamos imprimir o valor de um ponteiro desreferenciado, o que vemos é o conteúdo da variável para a qual ele aponta. O que isto quer dizer? Quando mandamos imprimir o valor de um ponteiro, o que vemos é um número escrito na base hexadecimal. Ou seja, quando imprimimos o valor de um ponteiro, imprimimos o que ele significa: um endereço de memória. Quando mandamos imprimir o valor de um ponteiro desreferenciado, o que vemos é o conteúdo da variável para a qual ele aponta. Ou seja, ao desreferenciar um ponteiro, podemos tanto ler o valor da variável apontada, quanto escrever no valor da variável apontada. Exemplos: Lendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Exemplos: Lendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Imprimindo os valores das variáveis: printf("%d", i); -> 50 printf("%p", pi); -> 0xFF00 Exemplos: Lendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Imprimindo os valores das variáveis: printf("%d", i); -> 50 printf("%p", pi); -> 0xFF00 Imprimindo os endereços das variáveis: printf("%p", &i); -> 0xFF00 printf("%p", &pi); -> 0xFF04 Exemplos: Lendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Imprimindo os valores das variáveis: printf("%d", i); -> 50 printf("%p", pi); -> 0xFF00 Imprimindo os endereços das variáveis: printf("%p", &i); -> 0xFF00 printf("%p", &pi); -> 0xFF04 Imprimindo os valores desreferenciados das variáveis: printf("%d", *i); -> sabe-se lá o que está em 0x0032 printf("%d", *pi); -> 50 Outros exemplos: Lendo 00 00 00 32 FF ... ... 00 FF 04 FF 06 B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; int** ppi = π int*** pppi = &ppi; Mais exemplos: Escrevendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Mais exemplos: Escrevendo 00 00 00 32 FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Escrevendo nos ponteiros desreferenciados: *i = 654; -> falha de segmentação Mais exemplos: Escrevendo 00 00 00 FA FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Escrevendo nos ponteiros desreferenciados: *i = 654; -> falha de segmentação *pi = 250; -> i passa a valer 250 Mais exemplos: Escrevendo 00 00 00 FA FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Escrevendo nos ponteiros desreferenciados: *i = 654; -> falha de segmentação *pi = 250; -> i passa a valer 250 Usando scanf() com ponteiros: scanf("%d", i); -> falha de segmentação Mais exemplos: Escrevendo ?? ?? ?? ?? FF ... ... 00 00 00 00 1D B3 22 70 0 x F F 0 0 int i = 50; int* pi = &i; Escrevendo nos ponteiros desreferenciados: *i = 654; -> falha de segmentação *pi = 250; -> i passa a valer 250 Usando scanf() com ponteiros: scanf("%d", i); -> falha de segmentação scanf("%d", pi); -> i passa a valer a entrada do usuário (??) FIM
Compartilhar