Aula04 ponteiros e conversao

Post on 05-Aug-2015

68 views 8 download

Transcript of Aula04 ponteiros e conversao

Ponteiros

Yuri Tavares dos Passos

Arquitetura de Von Neumann● Os computadores pessoais utilizam uma

arquitetura que é baseada na arquitetura de Von Neumann.

● Esta arquitetura se baseia na utilização de um mesmo bloco de memória para armazenar dados e código executável.

● O processador fica responsável por buscar código na mesma memória em que busca por um dado.

Arquitetura de Von Neumann

Busca e executa

Lê e escreve

Arquitetura de Von Neumann

Dados

Código

Código

Dados

Código

Dados

Memória Principal

Busca e executa

Lê e escreve

Arquitetura de Von Neumann● Suponha uma memória RAM com

capacidade de 8 B. Ela pode ser endereçada dos endereços 0 a 7, cada um com a capacidade de 1 B.

1 B

0x00x10x20x30x40x50x60x7

E se ela tivesse 1GB, iria doEndereço 0 a qual número decimal?

E hexadecimal?

Arquitetura de Von Neumann● Suponha uma memória RAM com

capacidade de 8 B. Ela pode ser endereçada dos endereços 0 a 7, cada um com a capacidade de 1 B.

1 B

0x00x10x20x30x40x50x60x7

E se ela tivesse 1GB, iria doEndereço 0 a qual número decimal?Resp.: 1073741823E em hexadecimal?Resp.: 3FFFFFFF

Arquitetura de Von Neumann

16

MOVE 16 0x19

SOME 0x21 0x19

3.89

MOVE 0x19 0x24

?

Memória PrincipalEndereços

0x19

0x21

0x20

0x22

0x23

0x24

Arquitetura de Von Neumann

00010000

01010101

10100010

00100111

01010101

00010000

Memória PrincipalEndereços

0x19

0x21

0x20

0x22

0x23

0x24

Arquitetura de Von Neumann

16

MOVE 16 0x19

SOME 0x21 0x19

3.89

MOVE 0x19 0x24

?

Memória PrincipalEndereços

0x19

0x21

0x20

0x22

0x23

0x24

Esta instrução envolveo uso de um endereço e um valor.

Esta instrução envolveapenas endereços.

Ponteiro

● Como manipular endereços de memória no código-fonte?– Utilizando um tipo de variável para endereços.

● Um ponteiro ou apontador é uma variável que guarda endereços de memória.

● Endereços de memória são valores inteiros que podem ser armazenados e utilizados no código.

Ponteiros

● Aplicações de ponteiros:– Estruturas de dados mais eficientes.

– Aplicações que envolvam a organização dos dados.

– Aplicações que devem ser rápidas.

– Alocação dinâmica de memória.

– Acessar variáveis que não são visíveis em uma função.

– Retornar mais de um valor para uma função.

● A tentativa de evitar o uso de ponteiros implicará quase sempre códigos maiores e de execução mais lenta.

Ponteiro

16

3.98

SOME 0x24 0x19

0x20

MOVE 0x19 0x24

0x22

0x19

0x21

0x20

0x22

0x23

0x24

São variáveis que apontam para outras

Ponteiro

● Nos exemplos acima, os endereços possuíam 1 B de tamanho.

● Quantos valores podem ser armazenados com 1 B?

Ponteiro

● Nos exemplos acima, os endereços possuíam 1 B de tamanho.

● Quantos valores podem ser armazenados com 1 B?– Resp.: 1 B = 8 b

28 = 256

● Quantos Bytes as memórias vendidas no comércio possuem?

Ponteiro

● Suponha um computador de 1 GB.● Ela pode possuir 1073741824 endereços de

1 B distintos. Para representar o maior endereço (3FFFFFFF), precisamos de 4 B.

● Portanto, para armarzenarmos um endereço numa máquina atual, precisaremos de 4 B ou mais!

Ponteiro

● Outro detalhe do exemplo de máquina apresentado é o tamanho dos nossos dados. Quantos bytes eles usam?

16

MOVE 16 0x19

SOME 0x21 0x19

3.89

MOVE 0x19 0x24

0

0x19

0x21

0x20

0x22

0x23

0x24

Ponteiro

● Qual tipo de dado em C possui 1 B?

Ponteiro

● Qual tipo de dado em C possui 1 B?● Resp.: char e unsigned char

Ponteiro

● Os tipos básicos de C possuem todos 1 B?

Tipos básicos Tamanho Precisão

char -> p/ caracteres 1 byte - 128 a + 127

int -> p/ inteiros 4 bytes* -2147483648 a 2147483647

float -> p/ reais 4 bytes* -3.4E-38 a +3.4E+38double -> p/ reais 8 bytes* -1.7E-308 a +1.7E+308

* Variam a depender do processador

Tipos de dados básicos

Ponteiro

● Considere o seguinte código:

Ponteiro

● Vamos pegar um número inteiro, como 0. Em uma célula de 4 B (= 32 b), temos como representação binária 32 zeros consecutivos.

● Um número inteiro como 1465232370 temos como representação binária 01010111010101011010101111110010– Em hexadecimal: 5755ABF2

Ponteiro

● Um caractere como 'a', na tabela de conversão ASCII é igual ao número 65. Como binário temos 01000001.– Em hexadecimal: 41

● O bloco de memória do código apresentado seria como apresentado a seguir.

Ponteiro

0xbff43513

0xbff43514

0xbff43515

0xbff43516

0xbff43517

0xbff43518

0xbff43519

0xbff4351a

0xbff4351b

00000000

00000000

00000000

00000000

01010111

01010101

10101011

11110010

01000001

} a = 0

} b = 1465232370

} c = 'a'

Ponteiro

● O endereço do inteiro a é 0xbff43513. Mas o inteiro vai de 0xbff43513 a 0xbff43516

● O endereço do caractere c é 0xbff4351b. Apenas este endereço é suficiente para guardá-lo.

● Além do endereço, o programa precisa saber o tamanho do dado que aquele endereço armazena.

Ponteiro

● Assim, um ponteiro além de armazenar o endereço de uma variável também possui o tamanho do dado daquela variável.

● Para se declarar uma variável do tipo ponteiro utiliza-se o símbolo * após o tipo de dado que este ponteiro deve apontar.

● A sintaxe de declaração é:

<tipo de dado> * <nome do ponteiro>;

Ponteiro

Ponteiro

● Para obter o endereço de memória de uma variável utiliza-se o operador &.

● A sintaxe deste operador é:

& <variável>

Ponteiro

Ponteiro

● Para acessar o conteúdo do que um ponteiro está apontando, usa-se *.

● A operação que realiza o acesso a algum dado apontado por um ponteiro é conhecida como desreferenciamento.– Um endereço de memória é uma referência a um

dado.

● Dentro de expressões, a sintaxe é:

* <variável ponteiro>

Ponteiro

Ponteiro

● Se fosse para escrever na tela os endereços onde as variáveis ponteiros pa, pb e pc estão o que deveria ser feito?

Ponteiro

Atribuições e tipos

● Em um compilador, o tipo de dado do lado esquerdo de uma atribuição deve ser igual ao tipo do resultado do lado direito.

● Exemplo 1:

c = 'b';

char char

Atribuições e tipos

● Exemplo 2:

a = 0 - b;

● Exemplo 3:

float f = 3.14 * 4.0 / a ;

int int

float float

Atribuições e tipos

● Quando utilizamos o operador &, o resultado é um endereço para a variável aplicada a este operador.

● Se temos:

&<variável>;

● O operador & retorna um tipo igual a “ponteiro do tipo da” <variável>.

Atribuições e tipos

● Para declaramos uma variável p1 ponteiro para o tipo caractere fazemos:

char * p1;

● Para declaramos uma variável p2 ponteiro para o tipo inteiro fazemos:

int * p2;

● Assim, podemos usar o * para denotar o tipo ponteiro de algum tipo.

Atribuições e tipos

● A regra de igualdade entre tipos nos dois lados de uma atribuição também funciona para ponteiros.

● Exemplo 1:

char c = 'a';char *p;p = &c; char* = & char

Sabendo-se que & <tipo> = <tipo>*, temos:

char* = char*

Atribuições e tipos

● Exemplo 2:

float f = 3.14;float *p;p = &f;

● Exemplo 3:

int b = 13;char * pi;pi = &b;

float* = & floatfloat* = float*

char* = & intchar* = int* ERRADO!

Atribuições e tipos

● Quando utilizamos o operador *, o resultado é um conteúdo para o mesmo tipo que o ponteiro deve apontar.

● Se temos:

* <variável ponteiro>;

● O operador * retorna um tipo igual ao tipo do ponteiro, sem um asterisco. Ou seja, ele remove o termo “ponteiro” para aquele tipo.

Atribuições e tipos

● Exemplo 1:– char c = 'b';

char d;char *p = &c;d = *p;

char = * char*Sabendo-se que * <ponteiro para tipo> = <tipo>, temos:char = char

O * em preto representa o operador desreferenciamento.

O * em vermelho representa o tipo “ponteiro de”.

Atribuições e tipos

● Exemplo 2:

int x = 9, y;int *p = &x;y = 2 + *p;

int = int + * int*int = int + intint = int

Qual o valor de y?

Atribuições e tipos

● Exemplo 3:

long l1 = 2E12;long *p1 = &l1;l1 = 2 * *p1;

long = long * * long*long = long * longlong = long

Qual o valor de l1?

Atribuições e tipos

● Exemplo 4:

long l1 = 2000, l2 = 0;long *p1, *p2;p1 = &l1;p2 = &l2;*p2 = 2 * *p1;

Existe algo de errado na última expressão?

Atribuições e tipos

● Exemplo 4:

long l1 = 2000, l2 = 0;long *p1, *p2;p1 = &l1;p2 = &l2;*p2 = 2 * *p1;printf(“%i\n”,l2);

Qual o valor aparecerá na tela?

Observação sobre * e &

● Observação– Os operadores & e * não podem ser apliados

a constantes.

● Exemplo:

int * p = &12;

– Isto não é possível pois a constante 12 não está armazenada em nenhum endereço.

Observação sobre * e &

● Exemplo:

char c = * 0x5623;

– Interpretando este exemplo em linguagem humana estaríamos dizendo que o valor de c será igual ao valor do byte que estiver no endereço 0x5623.

– Mas o S.O. protege este endereço, pois ele pode estar sendo utilizado por outro programa, evitando falha de segurança.

Ponteiro sem tipo

● Existe também um tipo de ponteiro que não está associado a nenhum tipo de variável: void *.

● Este tipo é utilizado quando se deseja acessar um endereço de memória ignorando o tipo de dado que existe neste endereço.

● Exemplo de declaração:

void * p;

void * bloco_de_memória;

● Contudo, atribuições para este tipo de variável necessitam de conversões explícitas para void*.

Conversão entre tipos

● Existem dois tipos de conversões– Implícitas

– Explícitas

● As conversões ímplicitas são feitas automaticamente pelo compilador, quando o tipo convertido é um subconjunto do tipo a ser atribuído.

Conversão implícita

● Exemplo:

int i = 1;float f = i;

Isto está errado?

Conversão implícita

● Exemplo:

int i = 1;float f = i;printf(“%f\n”,f);

Que valor será escrito na tela?

Conversão implícita

● Exemplo:

int i = 1;float f = i;

float int ℤ

Conversão implícita

● Exemplo:

int i = 1;float f = (float) i;

● Foi realizada uma conversão implícita de inteiro para real.

Conversão implícita

char

int

float

double

Conversão implícita

● O compilador gcc não acusa erro se for esquecida a conversão de um tipo mais abrangente para um tipo menos abrangente. Mas você será avisado caso esteja fazendo.

● Exemplo:

float f = 1.2;int i = f;

Conversão explícita

● A conversão explícita ocorre quando se deseja forçar a conversão entre tipos.

● Utiliza-se o nome do tipo a ser convertido entre parentêses.

● Exemplo:

float f = 1.2;int i = (int) f;

Conversão explícita

● É útil quando se sabe que tipo de dado está armazenado em uma certa variável, evitando os avisos dos compiladores.

● Deve ser usado com extrema sabedoria, pois dados podem ser perdidos!– 0.0000122323 ≠ 0.

● Para se utilizar o ponteiro sem tipo, deve ser utilizada esta conversão.– Exemplo:

char c = 'a';void* p = (void*) &c;

Incremento e decremento

● Ao se utilizar adição e subtração com ponteiros o comportamento destas operações serão diferentes do apresentado anteriormente.

● A soma e a subtração são realizadas em unidades que correspondem ao tamanho em bytes do tipo de dado que aquele ponteiro faz referência.

Incremento e decremento

● Exemplo 1:

Incremento e decremento

● No Exemplo 1, o valor do ponteiro pchar é aumentado de 1 como é feito normalmente com o ++.

● Veja que o valor de pchar foi aumentado de apenas mais uma unidade.

Incremento e decremento

● Exemplo 2:

Incremento e decremento

● Exemplo 3:

Exercícios

1) Escreva um programa com sizeof e verifique o tamanho dos seguintes tipos de dados: unsigned int *, int *, long*, float*, void*. Responda: qual o tamanho de cada um?

Exercícios

2) Por que o tamanho dos ponteiros da questão 1 são iguais apesar de serem ponteiros para tipos diferentes?

Exercícios

● Lista!