Buscar

Ponteiro: Acesso Direto à Memória

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 13 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 13 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 13 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.1 Segunda revisão / 2002 
 
VVII.. PPOONNTTEEIIRROOSS 
Ponteiro é um dos recursos mais importantes das linguagens C e C++. 
O uso de ponteiros permite o acesso direto ao conteúdo da memória do computador, a definição de 
variáveis (dos tipos intrínseco e derivado) em tempo de execução (armazenamento dinâmico) e passagem de 
parâmetros por referência (capítulos VII e VIII). 
No entanto, seu uso, sem uma clara compreensão de seu significado e conhecimento da forma correta de 
tratamento, pode levar a sérios danos na fase de execução de um programa, tais como, alterações de dados vitais, 
“corrupção” de programas e, até, de componentes do sistema operacional. Uma alteração inadvertida pode resultar 
em instabilidade do programa do usuário e/ou do sistema operacional, levando, neste último caso, ao “shutdown” 
forçado do computador. 
Neste capítulo, vamos tentar desmistificar os ponteiros, compreendendo seu significado e tentando 
indicar hábitos de programação que minimizam o uso incorreto de um recurso tão importante da linguagem. 
Ponteiro é um tipo de dado derivado não diferente, na sua natureza, de um número inteiro. Uma variável 
do tipo ponteiro armazena um número inteiro. Esse número inteiro corresponde a um endereço de memória (em 
microcomputadores com sistema operacional Windows de 32 bits, um ponteiro ocupa 4 bytes de memória). 
Quando o programador declara um identificador como do tipo ponteiro o programa passa a tratá-lo de 
forma diferenciada, apresentando seu conteúdo na notação hexadecimal e limitando o tipo de operações que 
podem ser executadas, como será visto na seção VI.C. 
O conteúdo de um ponteiro (ou seja, um endereço de memória) indica o local na memória no qual é 
armazenada a definição de uma outra variável ou uma função. Essa outra variável também pode ser do tipo 
ponteiro. Neste caso, tem-se a definição de um ponteiro para ponteiro, o que é denominado de indireção múltipla. 
Observações: 
Para que uma variável do tipo ponteiro contenha um endereço que tenha sentido no contexto da 
aplicação é necessário que esta seja inicializada convenientemente. A inicialização pode ser feita por 
atribuição direta, conforme apresentado neste capítulo (ver também o uso do operador &) ou pelo uso de 
funções, tais como, malloc e calloc, ou o operador new, apresentados no capítulo VII. 
 
Um ponteiro não inicializado é um erro de implementação muitas vezes difícil de ser localizado e que 
pode levar a graves erros durante a execução. 
 
Uma variável do tipo ponteiro não inicializada contém um endereço de memória que pode assumir 
qualquer valor. O uso da posição de memória indicada por tal variável, seja para efeito de leitura ou 
escrita, pode ter efeitos devastadores em tempo de execução (este tipo de problema não é identificado 
em tempo de compilação). 
 
Um programa que faz a leitura de dados de uma posição de memória qualquer estará utilizando um valor 
incorreto de uma variável. O uso dessa variável vai resultar em erros de cálculo. No caso do software de 
um sistema de controle pode resultar numa tomada de decisão incorreta. 
 
No caso de escrita, o problema também está presente. Uma gravação de dados numa região inapropriada 
de memória pode causar uma alteração em dados, no próprio programa, e/ou em componentes do 
sistema operacional. 
 
O exemplo a seguir procura explorar, a partir de declarações simples, o significado de um ponteiro e sua 
relação com o tipo de dado. 
 
Exemplo: 
 
short int id1 = 10; // define o identificador id1 de uma variável do tipo short int 
 short int * p_id1; // define a variável p_id1 do tipo ponteiro para short int (consulte a forma 
 // adequada para declaração de ponteiros na seção VI.A) 
 p_id1 = &id1; // atribui o endereço da variável id1 à variável p_id1 (veja o 
// uso do operador &, posteriormente, na seção VI.B) 
 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.2 Segunda revisão / 2002 
 
Vamos tentar compreender o que ocorre na memória do computador com o uso destas instruções. 
A variável id1, no exemplo anterior, ocupa dois bytes de memória já que é do tipo short int. A 
representação binária do valor de id1 é 
 0000 0000 0000 1010 
 
 
 
Vamos assumir que estamos trabalhando com um sistema operacional no qual o tamanho de um ponteiro 
é de 4 bytes. Vamos assumir, também, que o sistema armazenou o conteúdo de id1 a partir do endereço de 
memória 0xABAC0105, e o conteúdo da variável p_id1 (o ponteiro) a partir da posição de memória 
0xABAC010B. (Note que, na realidade, a atribuição de endereços às variáveis e funções é feita automaticamente 
pelo sistema). Desta forma, o conteúdo da variável ponteiro p_id1 será o endereço de memória no qual está 
armazenada a variável id1. Isto é representado como segue: 
 
primeiro byte
 
segundo byte
 
terceiro byte
 
quarto byte 
notação hexadecimal : A B A C 0 1 0 5 
notação binária 1010 1011 1010 1100 0000 0001 0000 0101 
 
 
 
A tabela a seguir mostra uma representação do conteúdo da memória. 
 
Tabela VI.1 : Representação do conteúdo da memória entre os endereços 0xABAC0100 e 0xABAC0110 
endereço de 
memória 
conteúdo da memória Comentários 
..... 
0xABAC0100 
0xABAC0101 
0xABAC0102 
0xABAC0103 
0xABAC0104 
0xABAC0105 0000 0000 a variável short int id1 ocupa dois bytes de memória (ou seja 
0xABAC0105 e 0xABAC0106): (primeiro byte) 0000 0000 
0xABAC0106 0000 1010 (segundo byte) 0000 1010 
0xABAC0107 
0xABAC0108 
0xABAC0109 
0xABAC010A 
0xABAC010B 1010 1011 primeiro byte do ponteiro (ponteiro de 4 bytes) 
0xABAC010C 1010 1100 segundo byte do ponteiro 
0xABAC010D 0000 0001 terceiro byte do ponteiro 
0xABAC010E 0000 0101 quarto byte do ponteiro 
0xABAC010F 
0xABAC0110 
... 
primeiro byte segundo byte 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.3 Segunda revisão / 2002 
 
 
VI.A. Declaração de uma variável do tipo ponteiro VI.A 
A declaração de um ponteiro apresenta a seguinte sintaxe: 
 
Sintaxe: 
tipo * ident_variavel; 
 
 
 
 
 
 
O uso do símbolo asterisco (*) na declaração é fundamental. Neste caso, ele atua como um indicador de 
que o identificador a seguir é um ponteiro para o tipo especificado (esta função do símbolo é diferente de seu uso 
como operador binário aritmético (de multiplicação – seção III.B.2) ou como operador unário de indireção. (seção 
III.A.5 e seção VI.B) 
Pode-se declarar uma variável ponteiro para qualquer tipo permitido em C e C++ (tipos intrínsecos e 
tipos derivados), inclusive tipos novos definidos por typedef (seçao I.B.3). Ponteiros para funções também 
podem ser declarados e definidos. 
 
Exemplos: 
 
// usa typedef para definição de um novo tipo de dado composto por um vetor de dois caracteres 
typedef char[2] WORD; 
 
int * p_var; // lê-se : p_var é o identificador de uma variável do tipo ponteiro para inteiros, 
 // ou, p_var é um ponteiro para uma variável do tipo inteiro, ou para um 
 // array de inteiros 
 
double * p_dvar; // p_dvar é um ponteiro para uma ou mais variáveis do tipo double 
 // armazenadas em seqüência na memória(um array de doubles) 
 
Matriz * po_matriz; // po_matriz é um ponteiro para um ou mais objetos do tipo Matriz armazenados 
 // em seqüência na memória (um vetor de Matrizes) 
 
WORD * p_word; // p_word é um ponteiro para uma variável do tipo WORD (ver typedef acima) 
VI.B. Operadores de Ponteiros VI.B 
São dois os operadores especiais para ponteiros definidos nas linguagens C e C++ . 
 
operador significado 
& 
O operador unário, aplicado à esquerda do operando, fornece o endereço, 
na memória, no qual o seu operando está armazenado. O valor obtido com 
a aplicação desse ponteiro pode ser associado somente a uma variável do 
tipo ponteiro. 
identificador da variável tipo válido (intrínseco ou 
derivado) 
na declaração, identifica uma variável ponteiro 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.4 Segunda revisão / 2002 
 
 
* 
O operador unário de conteúdo é aplicado à esquerda do operando (uma 
variável ponteiro declarada conforme apresentado na seção VI.A) e 
fornece o conteúdo armazenado na posição de memória indicada pelo 
ponteiro. 
 
 
Exemplo: 
 
char incorreto = 0; 
char correto = !incorreto; // observe o uso do operador unário de negação lógica 
char * p_inc = & incorreto; // define o ponteiro p_inc com a atribuição do endereço de incorreto 
char * p_cor = & correto; // define o ponteiro p_cor com a atribuição do endereço de correto 
 
cout << “\nvalor da variavel incorreto : “ << incorreto; 
cout << “\nvalor da variavel correto : “ << correto; 
 
cout << “\nvalor do conteúdo apontado por p_inc : “ << *p_inc; 
cout << “\nvalor do conteúdo apontado por p_cor : “ << *p_cor; 
 
Observe com cuidado, também, o uso do operador & no exemplo apresentado no início deste capítulo. 
VI.C. Expressões com ponteiros VI.C 
No geral, as regras de expressões de ponteiros são as mesmas que a de qualquer outra expressão. 
Contudo, são impostas restrições quanto ao tipo de operação que pode ser efetuada sobre uma variável ponteiro. 
VI.C.1.a. ATRIBUIÇÃO VI.C.1.a 
A uma variável ponteiro pode ser atribuído um endereço: 
 
int x; 
int *p1, *p2; // p1 e p2 são ponteiros 
p1 = &x; // o endereço da variável x é atribuído à variável ponteiro p1 
p2 = p1; // o conteúdo da variável ponteiro p1 (ou seja, o endereço de x) é atribuído à variável 
 // ponteiro p2 
VI.C.1.b. ARITMÉTICA 
São permitidas quatro operações aritméticas com ponteiros, a saber, incremento e decremento unários 
(++ e --), soma e subtração binários (+ e -). Estas operações são efetuadas de acordo com o tipo de dado com que 
o ponteiro está relacionado, ou seja, com a quantidade de memória que o tipo de dado ocupa, tabela VI.2. 
Tabela VI.2 : 
tipo de dado bytes ocupados número de bytes somados no 
incremento de 1 
(ponteiro++) 
número de bytes somados no 
incremento de 5 
(ponteiro = ponteiro + 5) 
char 1 1 1 * 5 = 5 
short int 2 2 2 * 5 = 10 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.5 Segunda revisão / 2002 
 
long int 4 4 4 * 5 = 20 
double 8 8 8 * 5 = 40 
vetor com 10 short int 10 * 2 = 20 20 20 * 5 = 100 
 
Exemplos: 
 
short int var = 10; 
short int *p_vari ; 
p_vari = &var; 
p_vari++; 
 
double dvar = 3.141592; 
double * p_vard; 
p_vard = &dvar; 
p_vard--; 
 
p_vari = p_vari – 8; 
p_vard = p_vard +2; 
 
Exercício: 
Assuma que as variáveis var e dvar são armazenadas nos endereços 0x00001000 e 0x00001500, 
respectivamente. Verifique qual o conteúdo dos ponteiros p_vari e p_vard linha a linha do exemplo acima. 
VI.C.1.c. COMPARAÇÃO ENTRE PONTEIROS 
É possível comparar logicamente dois ponteiros do mesmo tipo. Normalmente, comparações são 
utilizadas quando mais de um ponteiro faz referência a um mesmo dado. 
 
Exemplo : 
char * p_p, * p_q; 
....... // conjunto de instruções 
if (p == q) 
{ conjunto de instruções }; 
 
VI.D. Arrays e Ponteiros 
No capítulo V, os arrays foram apresentados como um agrupamento contínuo de um ou mais elementos 
de um mesmo tipo. Nesse capítulo foram apresentados, também, a forma de fazer acesso aos elementos do array 
por meio de índices e a forma como arrays são armazenados na memória. Sugere-se que o leitor faça uma revisão 
da seção V.A antes de continuar a leitura deste capítulo. 
O uso de ponteiros permite definir um segundo método de acesso a componentes de um array. 
Um ponteiro para um array é o ponteiro para o primeiro elemento de um array (ou seja, o ponteiro para 
um array aponta para o primeiro elemento do array). Observe atentamente os exemplos a seguir, acompanhando 
os comentários. 
 
Exemplo: 
int vet [10] = { 13,12,100,99,1000,999,1313,1212,21,31 }; // define o array identificado por vet 
int * p_vet = 0; // declara um ponteiro para inteiro, 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.6 Segunda revisão / 2002 
 
 // inicializando-o com zero; 
 // Sim, um ponteiro pode ser inicializado 
 // com zero, o que normalmente é feito 
 // para indicar que o ponteiro não aponta 
 // para nenhum endereço em especial 
int b, c; 
 
p_vet = &vet [0]; // atribui a p_vet o endereço do inicio do array vet 
p_vet = vet; // Esta instrução é equivalente à anterior. 
 // Normalmente, o nome do array é um “alias” 
 // (ou sinônimo) para o endereço do array; 
 
b = vet [5]; // atribui o sexto elemento de vet à variável b 
c = *(vet + 5); // atribui o conteúdo apontado por (vet + 5) a c 
 
if ( b == c ) cout << “b é igual a c”; 
else cout << “b e c são diferentes”; 
 
Exercícios: 
1. Qual o conteúdo apontado por (vet+3) e qual o apontado por (vet+9)? 
2. Qual o conteúdo apontado por (vet+10)? 
3. Escreva um programa completo em C++ (que seja compilável e que gere um executável) com as 
instruções do exemplo acima. 
4. Escreva um programa para soma dos dez primeiros números naturais armazenando-os primeiramente 
num array unidimensional. Utilize somente ponteiros para acesso ao array. 
 
Exemplo: 
double dvet [ 3 ] [ 3 ] = { { 13., 12., 100. }, // define o array bidimensional denominado 
 { 99., 1000., 999. }, // dvet 
 { 1313., 1212., 21. } // consultem a seção V.A.4 sobre o 
 }; // armazenamento de arrays na memória. 
 
double * p_dvet = 0; // define o ponteiro nulo (ou zero) p_dvet 
 
double b, c;p_dvet = &dvet [ 0 ] [ 0 ]; // atribui o endereço do primeiro elemento do 
 // array dvet ao ponteiro p_dvet 
 
cout << “conteúdo do sexto elemento de dvet é : “<< *(p_dvet + 5); 
 
Exercícios: 
1. Escreva um programa completo em C++ (que seja compilável e que gere um executável) com as 
instruções do exemplo acima. 
2. Modifique o programa para imprimir utilizando índices do array fornecidos pelo usuário (faça de 
duas formas, utilizando acesso por índices e por ponteiros). 
 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.7 Segunda revisão / 2002 
 
VI.E. Structures e Ponteiros 
Na seção V.B foram apresentados a declaração de structures e operadores de acesso a seus dados 
membros. Nesta seção, vamos declarar ponteiros para structures e verificar como é feito o acesso a seus dados 
membros, utilizando o operador de acesso −>. 
Compare os exemplos a seguir com os apresentados na seção V.B. Para facilitar a compreensão, os 
primeiros dois exemplos fazem uso das structs data e endereço, respectivamente. O terceiro exemplo envolve o 
uso das structs data, endereço e pessoa. 
Aproveite para observar, também, a tabulação efetuada nas instruções de cada bloco. A adoção de 
tabulações diferentes para indicar instruções em blocos diferentes facilita a visualização da organização do código. 
 
Exemplo 1: 
 
#include <iostream.h> 
 
struct data 
{ unsigned char dia, mes; unsigned short int ano;}; // fim da declaração da struct data 
 
void main(void) 
{ 
// definicao da data com identificador dia_formatura 
data dia_formatura = {30,2,2000}; 
 
//---------------------------------------------------------------------------------------------------- 
// declaração e inicialização de ponteiro nulo para struct data 
//---------------------------------------------------------------------------------------------------- 
data * p_data = 0; // ponteiro para uma struct data 
 
// acessando os dados membros da struct data por meio dos ponteiros 
p_data = & dia_formatura; // atribui a p_data o endereço de dia_formatura 
 // antes desta atribuição p_data não aponta para nada definido 
unsigned short int a; 
unsigned char d, m; 
d = p_data −> dia; 
m = p_data −> mes; 
a = p_data −> ano; 
 
// alteração dos dados membros da struct data 
a = 1998; 
d = 13, m = 4; 
p_data −> dia = d; 
p_data −> mes = m; 
p_data −> ano = a; 
 
cout << “\n data atualizada : “<< dia_formatura.dia << “/ “ << dia_formatura.mes 
 << “/ “ << dia_formatura.ano; 
cout << “\n data atualizada com acesso por ponteiro : “<<p_data −> dia << “/ “ 
 << p_data −> mes << “/ “ << p_data −> ano; 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.8 Segunda revisão / 2002 
 
 
} // fim da função main 
 
Exemplo 2: 
 
#include <iostream.h> 
 
struct endereco 
{ 
char rua [256]; 
unsigned int numero; 
char cidade [30]; 
unsigned long int cep; 
} ; // fim da declaração da struct endereco 
 
void main(void) 
{ 
//---------------------------------------------------------------------------------------------------- 
// definição de structs 
//---------------------------------------------------------------------------------------------------- 
endereço end = { “Rua Balalaica”, 12222, “São Paulo”, 12222000}; 
endereco novo_end = { “Av. BlaBla da Silva”, 000, “Seilá”, 00000000}; 
 
//---------------------------------------------------------------------------------------------------- 
// declaração e inicialização de ponteiro nulo para struct endereço 
//---------------------------------------------------------------------------------------------------- 
endereço * p_end = 0; // ponteiro para uma struct endereço 
 
// acessando os dados membros da struct endereço por meio dos ponteiros 
p_end = &end; // atribui o endereço de end para o ponteiro p_end 
 
cout << “acesso a struct end por ponteiro : “<<p_end −> rua << “, “ 
 << p_end −> numero << ‘\n’<<p_end −> cidade << ‘\n’ << p_end −> cep; 
 
p_end = &novo_end; // atribui o endereço de novo_end para o ponteiro p_end 
 
cout << “acesso a struct end por ponteiro : “<<p_end −> rua << “, “ 
 << p_end −> numero << ‘\n’<<p_end −> cidade << ‘\n’ << p_end −> cep; 
 
// alteração dos dados membros da struct endereço 
char r [500] = “Rua X”, cid [500] = “Mogi”; 
unsigned int n = 123; 
unsigned long int cep = 01000100; 
 
p_end −> numero = n; 
p_end −> cep = cep; 
strcpy (p_end −> rua, r); // a função strcpy copia o conteúdo do array 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.9 Segunda revisão / 2002 
 
 // unidimensional de caracteres r para o membro rua 
strcpy (p_end −> cidade, cid); // a função strcpy copia o conteúdo do array 
 // unidimensional de caracteres r para o membro rua 
} // fim da função main 
 
Exercício: 
1. Qual das structures foi modificada (end ou novo_end)? 
2. Implemente a saída de dados utilizando cout para confirmar sua resposta. 
 
Exemplo 3: 
 
#include <iostream.h> 
 
struct endereco 
{ 
char rua [256]; 
unsigned int numero; 
char cidade [30]; 
unsigned long int cep; 
} ; // fim da declaração da struct endereco 
 
struct data 
{ unsigned char dia, mes; unsigned short int ano;}; // fim da declaração da struct data 
 
struct pessoa 
{ 
char nome [128]; 
endereco residencia; 
endereco comercial; 
data nascimento; 
}; // fim da declaração da struct pessoa 
 
void main(void) 
{ 
unsigned short int a; 
unsigned char d, m; 
//---------------------------------------------------------------------------------------------------- 
// definição de structs 
//---------------------------------------------------------------------------------------------------- 
// definição de uma pessoa com identificador Joao_da_Silva 
pessoa Joao_da_Silva = { “João José da Silva”, “Av. BlaBla da Silva”, 0, “Seilá”, 0, 
 “Rua 1”, 333, “Seilá”, 01000000, {28,02,1900}}; 
 
//---------------------------------------------------------------------------------------------------- 
// declaração e inicialização de ponteiros nulos para structs 
//---------------------------------------------------------------------------------------------------- 
endereço * p_end = 0; // ponteiro para uma struct endereço 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.10 Segunda revisão / 2002 
 
data * p_data = 0; // ponteiro para uma struct data 
pessoa * p_pessoa = 0; // ponteiro para uma struct pessoa 
 
// acessando os dados membros da struct data por meio dos ponteiros 
p_end = & Joao_da_Silva .... residencia; // note que o operador unário de endereço, &, 
 // fornece o endereço do dado membro// endereço, e não o endereço de João da Silva 
p_pessoa = & Joao_da_Silva ; // aqui, o operador de endereço fornece o 
 // endereço da struct Joao_da_Silva. Observe a 
 // diferença na sintaxe! 
p_data = & p_pessoa −> nascimento; // o operador de endereço fornece o endereço 
 // da struct data nascimento (dado membro de 
 // pessoa). Observe que, como p_pessoa é um 
 // ponteiro, o acesso ao dado membro é feito 
 // utilizando o operador −> 
 
// acessando os dados membros da struct endereço 
cout << “nome : “ << p_pessoa −> nome << ‘\n’; 
cout << “endereço residencial : “ << p_end −> rua << “, “ 
 << p_end −> numero << ‘\n’<< p_end −> cidade << ‘\n’ << p_end −> cep 
 << ‘\n’; 
cout << “endereço residencial : “ << Joao_da_Silva.residencia.rua << “, “ 
 << Joao_da_Silva.residencia. numero << ‘\n’ 
 << Joao_da_Silva.residencia. cidade << ‘\n’ << Joao_da_Silva.residencia. cep 
 << ‘\n’; 
 
cout << “data de nascimento : “<< p_data −> dia << “/ “ 
 << p_data −> mes << “/ “ << p_data −> ano; 
 
p_end = & p_pessoa −> comercial; 
cout << “\n\nendereço comercial : “ << p_end −> rua << “, “ 
 << p_end −> numero << ‘\n’<< p_end −> cidade << ‘\n’ << p_end −> cep 
 << ‘\n’; 
 
} // fim da função main 
 
Exercicio: 
1. Modifique o programa do exemplo 3 para incluir a possibilidade do usuário, após ler os dados de 
pessoa, alterar o número e o CEP, tanto do endereço comercial como o da residência. Faça 
experimentos com ponteiros (consulte os exemplos anteriores neste capítulo e nos capítulos IV e V, 
se necessário). 
2. Implemente também a possibilidade do usuário modificar a data de nascimento da pessoa. 
 
Observe que a declaração de ponteiros para struct segue exatamente os modelos apresentados nas seções 
anteriores. 
VI.F. Array de ponteiros 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.11 Segunda revisão / 2002 
 
Vamos lembrar, inicialmente, que arrays são agrupamentos contínuos de dados de um mesmo tipo. Isto 
significa que cada elemento do array ocupa o mesmo número de bytes na memória. 
Em muitas aplicações, particularmente quando se trabalha com seqüência de caracteres (strings), o 
tamanho dos elementos individuais que comporiam o array podem assumir tamanhos variados. 
Uma forma de usar arrays neste caso, apesar da possível diferença de tamanho dos elementos, é 
identificar, dentre todos os elementos, aquele que apresenta o maior comprimento, em bytes, e reservar o mesmo 
espaço para todas as strings, mesmo que estas não venham a ocupar todo o espaço colocado à sua disposição. 
 
Exemplo: 
Queremos armazenar 3 strings : str1 = “Paulo VI”, str2 = “João Paulo II “, str3 = “Pio XI”, num array. A 
representação do array bidimensional será: 
‘P’ ‘a’ ‘u’ ‘l’ ‘o’ ‘ ’ ‘V’ ‘I’ ‘\0’ 
‘J’ ‘o’ ‘a’ ‘o’ ‘ ’ ‘P’ ‘a’ ‘u’ ‘l’ ‘o’ ‘ ’ ‘I’ ‘I’ ‘\0’ 
‘P’ ‘i’ ‘o’ ‘ ’ ‘X’ ‘I‘ ‘\0’ 
 
#include <iostream.h> // é necessário incluir este cabeçalho para utilizar cout << 
#include <string.h> // é necessário incluir este cabeçalho para utilizar a função strcpy 
void main (void) 
{ 
char str1[ ] ="Paulo VI"; // o compilador dimensiona, estaticamente, o array unidimen- 
// sional quando a inicialização ocorre com a declaração 
char str2[ ] ="João Paulo II"; 
char str3[ ] ="Pio XI"; 
 
char papas [ 3 ] [ 14 ] ; // declarado um array bidimensional do tipo char de 3 linhas por 14 
 // colunas 
strcpy (papas [0], str1); 
strcpy (papas [1], str2); 
strcpy (papas [2], str3); 
 
cout << "tamanho reservado a cada array de strings é 14 bytes\n"; 
 
// observe a seguir o uso do operador sizeof (seção III.A.8) 
cout << "tamanho realmente ocupado pela primeira string e : "<< sizeof ( str1 ) << "bytes\n"; 
cout << "tamanho realmente ocupado pela segunda string e : "<< sizeof ( str2 ) << "bytes\n"; 
cout << "tamanho realmente ocupado pela terceira string e : "<< sizeof ( str3 ) << "bytes\n"; 
 
cout << "primeira string : "<< papas[0] << "\n"; 
cout << "segunda string : "<< papas[1] << "\n"; 
cout << "terceira string : "<< papas[2] << "\n"; 
} 
 
Esta abordagem provoca um desperdício de memória que pode ser evitado com o uso de ponteiros, como 
apresentado no exemplo a seguir. 
 
Exemplo: 
#include <iostream.h> // é necessário incluir este cabeçalho para utilizar cout << 
#include <string.h> // é necessário incluir este cabeçalho para utilizar a função strcpy 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.12 Segunda revisão / 2002 
 
void main (void) 
{ 
char str1[ ] ="Paulo VI"; // o compilador dimensiona, estaticamente, o array unidimen- 
 // sional quando a inicialização ocorre com a declaração 
char str2[ ] ="João Paulo II"; 
char str3[ ] ="Pio XI"; 
 
char *papas [ 3 ] ; // declarado um array unidimensional de 3 ponteiros para char. Preste 
 // atenção na interpretação!! 
papas[0] = str1; // o endereço de str1 é armazenado em papas[0]. Note que papas[0] é 
 // um ponteiro e str1 também é (str1 é equivalente a &str1[0]) 
papas[1] = str2; // str2 é equivalente a &str2[0] 
papas[2] = str3; // str3 é equivalente a &str3[0] 
 
cout << "tamanho reservado a cada array de strings é 14 bytes\n"; 
 
// observe a seguir o uso do operador sizeof (seção III.A.8) 
cout << "tamanho realmente ocupado pela primeira string e : "<< sizeof ( str1 ) << "bytes\n"; 
cout << "tamanho realmente ocupado pela segunda string e : "<< sizeof ( str2 ) << "bytes\n"; 
cout << "tamanho realmente ocupado pela terceira string e : "<< sizeof ( str3 ) << "bytes\n"; 
cout << "primeira string : "<< papas[0] << "\n"; 
cout << "segunda string : "<< papas[1] << "\n"; 
cout << "terceira string : "<< papas[2] << "\n"; 
} // fim da função main 
VI.G. Indireção múltipla 
Como já foi comentado anteriormente, variáveis do tipo ponteiro podem apontar para elementos que 
também são ponteiros. Isto é denominado indireção múltipla (do inglês multiple indirection). Teoricamente, não 
existe limites para o número de indireções, mas cada nível de indireção traz um maior nível de complexidade ao 
programa. 
Compare o esquema de armazenamento na memória apresentado na tabela VI.3 com o esquema 
apresentado na tabela VI.1. 
 
Exemplo : 
 
// declaração de um ponteiro que aponta para um ponteiro que aponta para um valor inteiro 
// (dois níveis de indireção) 
 
int ** p_p_id1; 
int * p_id1; 
int id1 = 10; 
p_id1 = & id1; 
p_p_id1 = & p_id1; 
 
// declaração de um ponteiro que aponta para um ponteiro que aponta para uma 
// struct pessoa (seção V.B.I) (dois níveis de indireção) 
pessoa ** p_p_pessoa; 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VI.13 Segunda revisão / 2002 
 
 
A tabela VI.3 ilustra o que ocorre na memória quando da indireção em dois níveis de um inteiro. Os 
dados são os mesmos apresentados na tabela VI.1 . 
 
Tabela VI.3 : Representaçãodo conteúdo da memória entre os endereços 0x100 e 0x115 
endereço de 
memória 
conteúdo da 
memória 
comentários 
..... 
0xABAC0100 1010 1011 Neste endereço está armazenada a variável p_p_id1 (AB) 
0xABAC0101 1010 1100 ou seja, o ponteiro para um ponteiro que aponta para um inteiro (AC) 
0xABAC0102 0000 0001 (01) 
0xABAC0103 0000 1011 (0B) 
0xABAC0104 
0xABAC0105 0000 0000 a variável short int id1 ocupa dois bytes de memória (ou seja 
0xABAC0105 e 0xABAC0106): (primeiro byte) 0000 0000 
0xABAC0106 0000 1010 (segundo byte) 0000 1010 
0xABAC0107 
0xABAC0108 
0xABAC0109 
0xABAC010A 
0xABAC010B 1010 1011 primeiro byte do ponteiro (ponteiro de 4 bytes) (AB) 
0xABAC010C 1010 1100 segundo byte do ponteiro (AC) 
0xABAC010D 0000 0001 terceiro byte do ponteiro (01) 
0xABAC010E 0000 0101 quarto byte do ponteiro (05) 
0xABAC010F 
... 
 
VI.H. Saída formatada de ponteiros 
Para apresentar uma saída formatada em um dispositivo de saída padrão (monitor de vídeo), pode-se usar 
a função padrão do ANSI C printf. Observe a constante literal para definição do formato de impressão dos 
ponteiros do exemplo apresentado à seção VI.C.1.a: 
 
printf ((“p1 = %p, p2 = %p, \&x = % p” , p1, p2, &x); 
 
Neste caso, %p especifica que os parâmetros 1, 2 e 3 devem ser apresentados no formato de ponteiros.

Continue navegando