Você está na página 1de 110

Linguagem de Programação

1
Ponteiros

Objetivos:
Aprender a utilizar a estrutura básica de ponteiros.

2
Ponteiros:
Um ponteiro ou apontador é um tipo de dado de uma linguagem
de programação cujo valor se refere diretamente a um outro valor
alocado em outra área da memória, através de seu endereço.

O ponteiro vai armazenar o endereço de uma outra variável, a


variável na qual ele está apontando.

Então, é possível relacionar uma variável com um ponteiro através


do endereço, e assim, com o endereço da variável, é possível
manipular o seu conteúdo.

Por meio deste endereço pode-se acessar a informação, dizendo


que a variável ponteiro aponta para uma posição de memória.

O maior problema em relação ao ponteiro é entender quando se


está trabalhando com o seu valor, ou seja, o endereço, e quando se
está trabalhando com a informação apontada por ele.
3
Ponteiros – Operadores & e *:

O primeiro operador de ponteiro é &. Ele é um operador unário que


devolve o endereço na memória de seu operando.
Por exemplo:
m = &count;
põe o endereço na memória da variável count em m. Esse endereço é a
posição interna da variável na memória do computador e não tem
nenhuma relação com o valor de count. O operador & tem como
significado o endereço de.

4
Ponteiros – Operadores & e *:
O segundo operador é * , que é o complemento de &. O * é um operador
unário que devolve o valor da variável localizada no endereço que o
segue.
Por exemplo, se m contém o endereço da variável count:
q = *m; coloca o valor de count em q.
O operador * tem como significado no endereço de.

5
Ponteiros – Operadores & e *:

A declaração de uma variável ponteiro é dada pela colocação de


um asterisco (*) na frente de uma variável de qualquer tipo. Na
linguagem C, é possível definir ponteiros para os tipos básicos
ou estruturas.

A definição de um ponteiro não reserva espaço de memória para


o seu valor e sim para o seu conteúdo. Antes de utilizar um
ponteiro, o mesmo deve ser inicializado, ou seja, deve ser
colocado um endereço de memória válido para ser acessado
posteriormente.

6
Ponteiros – Operadores & e *:
Exemplo:

Para criar um ponteiro ele tem que ser do mesmo tipo da variável que
ele aponta.
- int *pt;
Como relacionar a variável num ao ponteiro:
- pt = #
Quando for colocado: cout<< pt; o que irá aparecer?

1000 que é o endereço da variável num.

7
Ponteiros – Operadores & e *:

Para imprimir o valor da variável num , deve-se fazer o seguinte:

cout<<*pt;

Nesse caso o valor que irá aparecer na tela é o 4.

Resumindo o ponteiro armazena o endereço de uma outra variável.

8
2. Operadores de ponteiro
Considerando as declarações:

int y = 5; // declara variável y


int *yPtr; // declara variável ponteiro yPtr

a instrução

yPtr = &y; // atribui o endereço de y a yPtr

atribui o endereço da variável y à variável ponteiro yPtr.

9
2. Operadores de ponteiro

Representação gráfica de um ponteiro que


aponta para uma variável na memória.

Representação de y e yPtr na memória.

10
A instrução

cout << *yPtr << endl;

imprime o valor de variável y, isto é, 5, assim como faria a


instrução

cout << y << endl;

Isto é chamado de desreferenciar um ponteiro.

11
Um ponteiro desreferenciado também pode ser utilizado no
lado esquerdo de uma instrução de atribuição, como em

*yPtr = 9;

que atribuiria 9 a y.

O ponteiro desreferenciado também pode ser utilizado para


receber um valor de entrada como em

cin >> *yPtr;

que coloca o valor de entrada em y.

12
Ponteiros – exemplo
#include <iostream>
using namespace std;

int main()
{
int num; // num é um inteiro
int *pt; // pt é um ponteiro int * -- para um inteiro

num = 4; // atribui 4 a num


pt = &num; // atribui o endereço de num ao pt

cout << "O endereco de num eh " << &num<< "\nO valor de pt eh " << pt;
cout << "\n\nO valor de num eh " << num << "\nO valor de *pt eh " << *pt;

cout << "\n\nMostrando que * e & sao inversos" << " um do outro.\n&*pt = " << &*pt
<< "\n*&pt = " << *&pt << endl;

system("pause");
return 0; // indica terminação bem-sucedida
} // fim de main

13
Ponteiros – exemplo
#include <iostream>
#include <string>
using namespace std;

int main()
{

string curso ="Engenharia";


string *pt;

pt=&curso;
cout<<"valor do ponteiro:"<<pt<<"\n";
cout<<"endereco da variavel curso :"<<&curso<<"\n";
cout<<"conteudo do ponteiro:"<<*pt<<"\n";
cout<<"conteudo da variavel curso :"<<curso<<"\n";

*pt="Automacao";

cout<<"conteudo do ponteiro:"<<*pt<<"\n";
cout<<"conteudo da variavel curso :"<<curso<<"\n";

system("pause");
return 0; 14
}
Ponteiros – exercícios

01- Escreva um programa que declare um inteiro, uma string e um


float, e ponteiros para inteiro, string, e float. Associe as variáveis
aos ponteiros (use &). Modifique os valores de cada variável
usando os ponteiros. Imprima os valores das variáveis antes e
após a modificação.

02 - Escreva um programa que contenha duas variáveis inteiras.


Compare seus endereços e exiba o maior endereço.

03 - Escreva um programa que contenha duas variáveis inteiras.


Em seguida, compare seus endereços e exiba o conteúdo do
menor endereço.

15
Passando argumentos para funções com ponteiros

Maneiras em C++ de passar argumentos a uma função:


• passagem por valor;
• passagem por referência com argumentos de referência; e
• passagem por referência com argumentos de ponteiro.

Quando os argumentos são passados para uma função usan-


do argumentos de referência, esses argumentos permitem
que a função chamada modifique os valores originais dos ar-
gumentos no chamador.

Os argumentos de referência também permitem aos progra-


mas passar grandes objetos de dados para uma função e evi-
tam o overhead de passar os objetos por valor (o que, natu-
ralmente, exige a produção de uma cópia do objeto).

16
Passando argumentos para funções com ponteiros

Os ponteiros, como as referências, também podem ser utiliza-


dos para modificar uma ou mais variáveis no chamador ou
passar ponteiros para objetos grandes de dados a fim de
evitar o overhead de passar os objetos por valor.

Em C++, os programadores podem utilizar ponteiros e o ope-


rador de indireção (*) para realizar a passagem por referência.

Ao chamar uma função com um argumento que deve ser mo-


dificado, o endereço do argumento é passado.

Isso normalmente é realizado aplicando o operador de ende-


reço (&) ao nome da variável cujo valor será modificado.

17
Passando argumentos para funções com ponteiros

Os arrays não são passados com operador &, porque o nome


do array é a posição inicial na memória do array (isto é, um
nome de array já é um ponteiro).

O nome de array, arrayName, equivale a &arrayName[ 0 ].

Quando o endereço de uma variável é passado a uma função,


o operador de indireção (*) pode ser utilizado na função para
formar um sinônimo para o nome da variável.

Isso por sua vez pode ser utilizado para modificar o valor da
variável naquela localização na memória do chamador.

Os exemplos seguintes mostram duas versões de uma função


que eleva um inteiro ao cubo.
18
Passando argumentos para funções com ponteiros

// Eleva uma variável ao cubo utilizando passagem por valor.


#include <iostream>
using std::cout;
using std::endl;

int cubeByValue( int ); // protótipo

int main()
{
int number = 5;
cout << “O valor original ne number é “ << number;

number = cubeByValue( number );


cout << “\nO novo valor de number é “ << number
<< endl;
19
Passando argumentos para funções com ponteiros

return 0;
} // fim de main

int cubeByValue( int n )


{
return n * n * n;
}

O valor original de number é 5


O novo valor de number é 125

20
Passando argumentos para funções com ponteiros

// Eleva uma variável ao cubo utilizando passagem por valor.


#include <iostream>
using std::cout;
using std::endl;

void cubeByReference( int * ); // protótipo

int main()
{
int number = 5;
cout << “O valor original ne number é “ << number;

cubeByReference( &number );
cout << “\nO novo valor de number é “ << number
<< endl;
21
Passando argumentos para funções com ponteiros

return 0;
} // fim de main

void cubeByReference( int *nPtr )


{
*nPtr = *nPtr * *nPtr * *nPtr;
}

O valor original de number é 5


O novo valor de number é 125

O cabeçalho da função cubeByReference especifica que ela


recebe o endereço de uma variável int (isto é, um ponteiro
para um int) como um argumento, armazena o endereço lo-
calmente em nPtr e não retorna um valor.
22
Passando argumentos para funções com ponteiros

23
Passando argumentos para funções com ponteiros

24
Passando argumentos para funções com ponteiros

25
Passando argumentos para funções com ponteiros

No cabeçalho e no protótipo de uma função que espera um


array unidimensional como um argumento, pode-se utilizar a
notação de ponteiro na lista de parâmetros.

O compilador não diferencia entre uma função que recebe um


ponteiro e uma que recebe um array unidimensional: isso quer
dizer que a função deve ‘saber’ quando está recebendo um
array ou uma única variável passada por referência.

Quando o compilador encontra um parâmetro de função para


um array unidimensional na forma int b[], o compilador con-
verte o parâmetro em notação de ponteiro int *b.

Ambas as formas de declarar um parâmetro de função como


um array dimensional são intercambiáveis.
26
Utilizando const com ponteiros

Se um valor não muda (ou não deve mudar) no corpo de uma


função para o qual ele é passado, o parâmetro deve ser de-
clarado como const para assegurar que ele não seja modifi-
cado acidentalmente.

Antes de utilizar uma função, verifique seu protótipo de função


para determinar os parâmetros que ele pode modificar.

Há quatro maneiras de passar um ponteiro para uma função:

• um ponteiro não constante para dados não constantes;


• um ponteiro não constante para dados constantes;
• um ponteiro constante para dados não constantes; e
• um ponteiro constante para dados constantes.

27
Utilizando const com ponteiros

A declaração de um ponteiro não constante para dados não


constantes não inclui const.

Assim, os dados podem ser modificados pelo ponteiro desre-


ferenciado e o ponteiro pode ser modificado para apontar para
outros dados.

Esse ponteiro pode ser utilizado para receber uma string ter-
minada por caractere nulo em uma função que altera o valor
de ponteiro para processar (e, possivelmente, modificar) cada
caractere na string.

O código de programa seguinte detalha este tipo de aplicação.

28
Utilizando const com ponteiros

// Convertendo minúsculas em maiúsculas utilizando


// um ponteiro não constante para dados não constantes.
#include <iostream>
using std::cout;
using std::endl;

#include <cctype> // protótipos para islower e toupper


using std::islower;
using std::toupper;

void convertToUppercase( char * );

int main()
{
char phrase[] = “caracteres e R$32,98”;
29
Utilizando const com ponteiros

cout << “A fase antes da conversao eh: ” << phrase;


convertToUppercase( phrase );
cout << “\nA frase depois da conversao eh: ” << phrase
<< endl;
return 0; // indica terminação bem-sucedida
} // fim de main

void convertToUppercase( char *sPtr ){


while ( *sPtr != ‘\0’ )
{
if ( islower( *sPtr ) )
*sPtr = toupper( *sPtr );
sPtr++;
}
}
30
Utilizando const com ponteiros

Um ponteiro constante para dados não constantes é um pon-


teiro que sempre aponta para a mesma posição da memória;
os dados nessa posição podem ser modificados pelo ponteiro.

Esse é o padrão de um nome de array, ou seja, um ponteiro


constante para o começo do array.

Todos os dados no array podem ser acessados e alterados


utilizando o nome do array e o subscrito do array.

Um ponteiro constante para dados não constantes pode ser


utilizado para receber um array como um argumento para uma
função que acessa elementos do array utilizando a notação de
subscrito do array.

31
Utilizando const com ponteiros

// Tentar modificar um ponteiro constante para dados não


// constantes.

int main()
{
int x, y;

int * const ptr = &x; // ponteiro const deve ser inicializado

*ptr = 7; // permitido: *ptr não é const


ptr = &y; // erro: ptr é constante; não é possível atribuí-lo a
// um novo endereço
return 0; // indica terminação bem-sucedida
} // fim de main

32
Classificação por seleção

#include <iostream>
using std::cout;
using std::endl;

#include <iomanip>
using std::setw;

void selectionSort( int * const, const int ); // protótipo


void swap( int * const, int * const ); // protótipo

int main()
{
const int arraySize = 10;
int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };

33
5. Classificação por seleção
Classificação por seleção

cout << “Dados na ordem original\n”;

for ( int i = 0; i < arraySize; i++ )


cout << setw( 4 ) << a[ i ];

selectionSort( a, arraySize ); // classifica o array

cout << “\nDados em ordem crescente\n”;

for ( int j = 0; j < arraySize; j++ )


cout << setw( 4 ) << a[ j ];

cout << endl;


return 0; // indica terminação bem-sucedida
} // fim de main
34
Classificação por seleção

// função para classificar um array


void selectionSort( int * const array, const int size )
{
int smallest; // índice do menor elemento
// itera sobre size - 1 elementos
for ( int i = 0; i < size - 1; i++ )
{
smallest = i; // primeiro índice do array remanescente

// faz um loop para localizar o índice do menor elemento


for ( int index = i + 1; index < size; index++ )
{
if ( array[ index ] < array[ smallest ] )
smallest = index;
}
35
5. Classificação por seleção
Classificação por seleção

swap( &array[ i ], &array[ smallest ] );


} // fim do for
} // fim da função selectionSort

// troca os valores nas posições da memória para as quais


// element1Ptr e element2Ptr apontem
void swap( int * const elem1Ptr, int * const elem2Ptr )
{
int hold = *elem1Ptr;
*elem1Ptr = *elem2Ptr;
*elem2Ptr = hold;
} // fim da função swap

36
Operadores sizeof

O C++ fornece o operador unário sizeof para determinar o


tamanho de um array (ou de qualquer outro tipo de dados,
variável ou constante) em bytes durante a compilação de
programa.

Quando aplicado ao nome de um array, retorna o número total


de bytes no array como um valor de tipo size_t(um alias para
unsigned int).

Observe que esse é diferente do size de um vector< int >,


por exemplo, que é o número de elementos do tipo inteiro no
vetor.

Quando aplicado a um parâmetro de ponteiro em uma função


que recebe um array como um argumento, sizeof retorna o
tamanho do ponteiro em bytes, não o tamanho do array. 37
Operadores sizeof

#include <iostream>
using std::cout;
using std::endl;

size_t getSize( double * ); // protótipo

int main()
{
double array[ 20 ]; // 20 doubles; o que ocupa 160 bytes

cout << “Número de bytes no array: ” << sizeof( array );

cout << “\nNúmero de bytes retornado por getSize: ”


<< getSize( array ) << endl;

38
Operadores sizeof

return 0; // indica terminação bem-sucedida


} // fim de main

// retorna o tamanho de ptr


size_t getSize( double *ptr )
{
return sizeof( ptr );
} // fim da função getSize

Número de bytes no array: 160


Número de bytes retornado por getSize: 4

39
Operadores sizeof

O número de elementos em um array também pode ser deter-


minado utilizando os resultados de duas operações sizeof.

Considere a seguinte declaração de array:

double realArray[ 22 ];

Se variáveis do tipo de dados double forem armazenadas em


oito bytes de memória, realArray conterá 176 bytes.

Para determinar o número de elementos no array, a seguinte


expressão pode ser utilizada:

sizeof realArray / sizeof( double )

40
Operadores sizeof

A expressão determina o número de bytes em realArray (176)


e divide esse valor pelo número de bytes utilizados na
memória para armazenar um valor double (8); o resultado é o
número de elementos em realArray (22).

Observe que os parênteses utilizados com sizeof só são


requeridos se o nome de um tipo (por exemplo, int) for forne-
cido como seu operando.

Os parênteses utilizados com sizeof não são requeridos


quando o operando for um nome de variável ou constante.

Como sizeof é um operador e não uma função, ele tem seu


efeito em tempo de compilação, não em tempo de execução.

41
Expressões e Aritmética de Ponteiro

Os ponteiros são operandos válidos em expressões aritméti-


cas, expressões de atribuição e expressões de comparação.

Entretanto, nem todos os operadores normalmente utilizados


nessas expressões são válidos com variáveis ponteiro.

Suponha que o array int v[ 5 ] tenha sido declarado e que seu


primeiro elemento esteja na posição da memória 3000.

42
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

Suponha que o ponteiro vPtr tenha sido inicializado para


apontar para v[ 0 ] (isto é, o valor de vPtr é 3000).

Observe que vPtr pode ser inicializado para apontar para o


array v com qualquer uma das seguintes instruções (porque o
nome de um array é equivalente ao endereço de seu primeiro
elemento):

int *vPtr = v;
int *vPtr = &v[ 0 ];

Quando um inteiro é adicionado a, ou subtraído de, um pontei-


ro, o ponteiro simplesmente não é incrementado ou decre-
mentado por esse inteiro, mas por esse inteiro vezes o tama-
nho do objeto que o ponteiro referencia.
43
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

Por exemplo, a instrução

vPtr += 2;

produziria 3008 (3000 + 2 * 4), supondo que um int é armaze-


nado em quatro bytes de memória.

No array v, vPtr agora apontaria para v[ 2 ].

44
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

Cada uma das instruções

++vPtr;
vPtr++;

incrementa o ponteiro para apontar para o próximo elemento


do array.

Cada uma das instruções

--vPtr;
vPtr--;

decrementa o ponteiro para apontar para o elemento anterior


do array.
45
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

As variáveis ponteiro que apontam para o mesmo array po-


dem ser subtraídas uma da outra.

Por exemplo, se vPtr contiver a posição 3000 e v2Ptr contiver


o endereço 3008, a instrução

x = v2Ptr - vPtr;

atribuiria a x o número de elementos do array de vPtr a v2Ptr


– nesse caso, 2.

Subtrair ou comparar dois ponteiros que não referenciam ele-


mentos do mesmo array é um erro de lógica.

46
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

Utilizar aritmética de ponteiros para incrementar ou decremen-


tar um ponteiro de modo que esse ponteiro referencie um ele-
mento depois do fim, ou antes do começo do array, é normal-
mente um erro de lógica.

Um ponteiro pode ser atribuído a outro ponteiro se ambos


forem do mesmo tipo.

Caso contrário, um operador de coerção deve ser utilizado


para converter o valor do ponteiro à direita da atribuição no
tipo de ponteiro à esquerda da atribuição.

Exemplo de operador de coerção:

static_cast< double >( total );


47
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

A exceção a essa regra é o ponteiro para void (isto é, void *),


que é um ponteiro genérico capaz de representar qualquer
tipo de ponteiro.

Um ponteiro de tipo void * sem coerção pode ser atribuído a


todos os tipos de ponteiro.

Mas um ponteiro do tipo void * não pode ser atribuído direta-


mente a um ponteiro de outro tipo – o ponteiro do tipo void *
deve primeiro sofrer coerção ao tipo de ponteiro adequado.

Todas as operações em um ponteiro void * são erros de com-


pilação, exceto comparar ponteiros void * com outros pontei-
ros, fazendo coerção dos ponteiros void * para tipos de pon-
teiros válidos e atribuindo endereços a ponteiros void *.
48
Expressões
7. Expressões e Aritmética
e Aritmética de Ponteiro
de Ponteiro

Os ponteiros podem ser comparados utilizando operadores de


igualdade e operadores relacionais.

As comparações que utilizam operadores relacionais não têm


sentido a menos que os ponteiros apontem para membros do
mesmo array.

Uma comparação de dois ponteiros que apontam para o mes-


mo array poderia mostrar, por exemplo, que um ponteiro
aponta para um elemento de número mais alto no array do
que o outro ponteiro.

Uma utilização comum da comparação de ponteiro é determi-


nar se um ponteiro é 0 (isto é, o ponteiro é um ponteiro nulo –
ele não aponta para nada).
49
Ponteiros – exemplo
#include <iostream>
#include <string>
using namespace std;

int main()
{

int *pt;
int vetor[10];

pt=&vetor[0];
cout<<"\n"<<pt<<endl;

pt=&vetor[1];
cout<<"\n"<<pt<<endl;

pt=&vetor[2];
cout<<"\n"<<pt<<endl;

system("pause");
return 0;
}
50
Ponteiros – exemplo
#include <iostream>
#include <string>
using namespace std;

int main()
{

int *pt;
int vetor[10];

pt=&vetor[0];
cout<<"\n"<<pt<<endl;

*(pt++);
cout<<"\n"<<pt<<endl;

*(pt++);
cout<<"\n"<<pt<<endl;

system("pause");
return 0;
}
51
Ponteiros – exemplo

52
Relacionamento entre Ponteiros e Arrays

// Utilizando notações de subscrito e de ponteiro com arrays.


#include <iostream>
using std::cout;
using std::endl;

int main()
{
int b[] = { 10, 20, 30, 40 }; // cria o array b de 4 elementos
int *bPtr = b; // configura bPtr para apontar para o array b

cout << “Array b impresso com:\n\n”


cout << “Array notação de subescrito\n”;

for ( int i = 0; i < 4; i++ )


cout << “b[” << i << “] = ” << b[ i ] << ‘\n’;
53
Relacionamento entre Ponteiros e Arrays

// gera saída do array b utilizando a notação de nome de


// array e a de ponteiro/deslocamento
cout << “\nNotação ponteiro/deslocamento em que ”
<< “o ponteiro é o nome do array\n”;

for ( int offset1 = 0; offset1 < 4; offset1++ )


cout << “*(b + ” << offset1 << “) = ”
<< *( b + offset1 ) << ‘\n’;

// gera saída do array b utilizando bPtr e notação de


// subscrito de array
cout << “\nNotação de subescrito de ponteiro\n”;

for ( int j = 0; j < 4; j++ )


cout << “bPtr [” << j << “] = ” << bPtr[ j ] << ‘\n’;
54
Relacionamento entre Ponteiros e Arrays

cout << “\nNotação ponteiro/deslocamento\n”;

// gera saída do array b utilizando bPtr e notação de


// ponteiro/deslocamento
for ( int offset2 = 0; offset2 < 4; offset2++ )
cout << “*(bPtr + ” << offset2 << “) = ”
<< *( bPtr + offset2 ) << ‘\n’;

return 0; // indica terminação bem-sucedida


} // fim de main

Esse programa utiliza quatro notações distintas para referen-


ciar os elementos de um array.

55
Relacionamento entre Ponteiros e Arrays

// Copiando uma string utilizando a notação de array e a


// notação de ponteiro.
#include <iostream>
using std::cout;
using std::endl;

void copy1( char *, const char * );


void copy2( char *, const char * );

int main()
{
char string1[ 10 ];
char *string2 = “Hello”;
char string3[ 10 ];
char string4[] = “Good Bye”;
56
Relacionamento entre Ponteiros e Arrays

copy1( string1, string2 ); // copia string2 para string1


cout << “string1 = ” << string1 << endl;

copy2( string3, string4 ); // copia string4 para string3


cout << “string3 = ” << string3 << endl;
return 0; // indica terminação bem-sucedida
} // fim de main

void copy1( char * s1, const char * s2 )


{
// a cópia ocorre no cabeçalho do for
for ( int i = 0; ( s1[ i ] = s2[ i ] ) != ‘\0’; i++ )
; // não faz nada no corpo
} // fim da função copy1

57
Relacionamento entre Ponteiros e Arrays

void copy2( char *s1, const char *s2 )


{
// a cópia ocorre no cabeçalho do for
for ( ; ( *s1 = *s2 ) != ‘\0’; s1++, s2++ )
; // não faz nada no corpo
} // fim da função copy2

string1 = Hello
string2 = Good Bye

Para ilustrar ainda mais a intercambialidade de arrays e pon-


teiros, esse programa mostra duas funções de copiar strings –
copy1 e copy2.

58
Arrays de Ponteiros

Os arrays podem conter ponteiros.

Uma utilização comum dessa estrutura de dados é formar um


array de strings baseadas em ponteiro, referido simplesmente
como um array de string.

Toda entrada no array é uma string, mas em C++ uma string é


essencialmente um ponteiro para seu primeiro caractere, en-
tão cada entrada em um array de strings é simplesmente um
ponteiro para o primeiro caractere de uma string.

Considere a declaração de array de string suit que poderia


ser útil na representação de um baralho:

59
Arrays de Ponteiros

const char *suit[ 4 ] =


{ “Hearts”, “Diamonds”, “Clubs”, “Spades” };

suit[4] indica um array de quatro elementos.

A parte const char * da declaração indica que cada elemento


do array é do tipo ‘ponteiro para dados char constantes’.

Os quatro valores a ser colocados no array são “Hearts”,


“Diamonds”, “Clubs” e “Spades”.

Embora pareça que essas strings estão sendo colocadas no


array suit, somente os ponteiros são realmente armazenados
no array, como mostrado na figura seguinte.

60
Arrays de Ponteiros

Cada ponteiro aponta para o primeiro caractere de sua string


correspondente.

Portanto, mesmo que o array suit tenha tamanho fixo, ele for-
nece acesso a strings de caractere de qualquer comprimento.

Essa flexibilidade é um exemplo das poderosas capacidades


da estrutura de dados do C++.

61
Embaralhamento e Distribuição de Cartas

// DeckOfCards.h
// Definição da classe DeckOfCards que representa
// um baralho.

// Definição da classe DeckOfCards


class DeckOfCards
{
public:
DeckOfCards(); // construtor inicializa deck
void shuffle(); // embaralha as cartas do baralho
void deal(); // distribui as cartas do baralho
private:
int deck[ 4 ][ 13 ]; // representa o baralho de cartas
}; // fim da classe DeckOfCards

62
Embaralhamento e Distribuição de Cartas

// main.cpp
// Programa de embaralhamento e distribuição de cartas.
#include “DeckOfCards.h” // Classe DeckOfCards

int main()
{
DeckOfCards deckOfCards; // cria objeto DeckOfCards

deckOfCards.shuffle(); // embaralha as cartas


deckOfCards.deal(); // distribui as cartas
return 0; // indica terminação bem-sucedida
} // fim de main

63
Embaralhamento e Distribuição de Cartas

// DeckOfCards.cpp
// Definições de função-membro para a classe DeckOfCards
// que simula o embaralhamento e distribuição de um baralho.
#include <iostream>
using std::cout;
using std::left;
using std::right;

#include <iomanip>
using std::setw;

#include <cstdlib> // protótipos para rand e srand


using std::rand;
using std::srand;

64
Embaralhamento e Distribuição de Cartas

#include <ctime> // protótipo para time


using std::time;

#include “DeckOfCards.h” // Classe DeckOfCards

// construtor
DeckOfCards::DeckOfCards() {
for ( int row = 0; row <= 3; row++ ) {
for ( int column = 0; column <= 12; column++ ) {
deck[ row ][ column ] = 0;
} // fim do for interno
} // fim do for externo

srand( time( 0 ) ); // semeia o gerador de número aleatório


} // fim do construtor
65
Embaralhamento e Distribuição de Cartas

void DeckOfCards::shuffle() {
int row; // representa o valor do naipe da carta
int column; // representa o valor da face da carta

// para cada uma das 52 cartas, escolhe um slot


for ( int card = 1; card <= 52; card++ ) {
do {
row = rand() % 4; // seleciona a linha
column = rand() % 13; // seleciona a coluna
} while( deck[ row ][ column ] != 0 ); // fim de do...while

// coloca o número de carta no slot escolhido


deck[ row ][ column ] = card;
} // fim do for
} // fim da função shuffle
66
Embaralhamento e Distribuição de Cartas

void DeckOfCards::deal() {
static const char *suit[ 4 ] =
{ “Ouros”, “Copas”, “Paus”, “Espadas” };

static const char *face[ 13 ] = { “Ás”, “Dois”, “Três”,


“Quatro”, “Cinco”, “Seis”, “Sete”, “Oito”, “Nove”,
“Dez”, “Valete”, “Rainha”, “Rei” };

for ( int card = 1; card <= 52; card++ ) {


for ( int row = 0; row <= 3; row++ ) {
for ( int column = 0; column <= 12; column++ ) {
if ( deck[ row ][ column ] == card )
{

67
Embaralhamento e Distribuição de Cartas

cout << setw( 5 ) << right << face[ column ]


<< “ de “ << setw( 8 ) << left << suit[ row ]
<< ( card % 2 == 0 ? ‘\n’ : ‘\t’ );
column = 12;
row = 3;
} // fim do if
} // fim do for mais interno
} // fim do for interno
} // fim do for externo
} // fim da função deal

68
Ponteiros de Função

Um ponteiro para uma função contém o endereço da função


na memória, sendo que o nome de uma função é o endereço
inicial na memória do código que realiza a tarefa da função.

Os ponteiros para funções podem ser passados para funções,


retornados de funções, armazenados em arrays e atribuídos a
outros ponteiros de função.

Para ilustrar o uso de ponteiros para funções, o programa se-


guinte modifica o programa de classificação por seleção.

A função selectionSort agora recebe um ponteiro para uma


função – uma função ascending ou descending – como um
argumento além do array de inteiros a classificar e o tamanho
do array.
69
11. Ponteiros de Função de Função
Ponteiros

// Programa de classificação para múltiplos propósitos usando


// ponteiros de função.
#include <iostream>
using std::cout;
using std::cin;
using std::endl;

#include <iomanip>
using std::setw;

void selectionSort( int [], const int, bool (*)( int, int ) );
void swap( int * const, int * const );
bool ascending( int, int ); // implementa ordem crescente
bool descending( int, int ); // implementa ordem decrescente

70
11. Ponteiros de Função de Função
Ponteiros

int main()
{
const int arraySize = 10;
int order; // 1 = crescente, 2 = decrescente
int counter; // índice do array
int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };

cout << “Enter 1 to sort in ascending order,\n”


<< “Enter 2 to sort in descending order: “;
cin >> order;
cout << “\nData items in original order\n”;

for ( counter = 0; counter < arraySize; counter++ )


cout << setw( 4 ) << a[ counter ];

71
11. Ponteiros de Função de Função
Ponteiros

if ( order == 1 ) {
selectionSort( a, arraySize, ascending );
cout << “\nData items in ascending order\n”;
} // fim do if
else {
selectionSort( a, arraySize, descending );
cout << “\nData items in descending order\n”;
} // fim da parte else do if...else

for ( counter = 0; counter < arraySize; counter++ )


cout << setw( 4 ) << a[ counter ];

cout << endl;


return 0; // indica terminação bem-sucedida
} // fim de main
72
11. Ponteiros de Função de Função
Ponteiros

void selectionSort( int work[], const int size,


bool (*compare)( int, int ) )
{
int smallestOrLargest;

for ( int i = 0; i < size - 1; i++ )


{
smallestOrLargest = i; // primeiro índice do vetor restante
for ( int index = i + 1; index < size; index++ )
if ( !(*compare)( work[ smallestOrLargest ],
work[ index ] ) )
smallestOrLargest = index;
swap( &work[ smallestOrLargest ], &work[ i ] );
} // fim do if
} // fim da função selectionSort
73
Ponteiros de Função

void swap( int * const element1Ptr,


int * const element2Ptr )
{
int hold = *element1Ptr;
*element1Ptr = *element2Ptr;
*element2Ptr = hold;
} // fim da função swap

bool ascending( int a, int b ){


return a < b; // retorna true se a for menor que b
} // fim da função ascending

bool descending( int a, int b ){


return a > b; // retorna true se a for maior que b
} // fim da função descending
74
11. Ponteiros de Função de Função
Ponteiros

O parâmetro a seguir aparece no cabeçalho de selectionSort


e especifica um ponteiro para uma função:

bool ( *compare )( int, int )

O parâmetro correspondente no protótipo desta função é:

bool (*)( int, int )

A função passada para selectionSort é assim chamada:

( *compare )( work[ smallestOrLargest ], work[ index ] )

Neste caso, o ponteiro para a função é desreferenciado para


executar a função.
75
11. Ponteiros de Função de Função
Ponteiros

A chamada à função poderia ter sido feita sem desreferenciar


o ponteiro, como em

compare( work[ smallestOrLargest ], work[ index ] )

que utiliza o ponteiro diretamente como o nome de função.

Entretanto, isso faz parecer que compare seria o nome de


uma função real no programa, o que pode ser confuso.

Uma utilização de ponteiros de função é em sistemas


baseados em menus, em que a escolha do usuário pode ser
utilizada como um subscrito para um array de ponteiros de
função e o ponteiro no array pode ser utilizado para chamar a
função.
76
11. Ponteiros de Função de Função
Ponteiros

// Demonstrando um array de ponteiros para funções.


#include <iostream>
using std::cout;
using std::cin;
using std::endl;

void function0( int );


void function1( int );
void function2( int );

int main()
{
void (*f[ 3 ])( int ) = { function0, function1, function2 };

int choice;
77
11. Ponteiros de Função de Função
Ponteiros

cout << “Entre um número entre 0 e 2, 3 para sair: ”;


cin >> choice;

while ( ( choice >= 0 ) && ( choice < 3 ) )


{
(*f[ choice ])( choice );

cout << “Entre um número entre 0 e 2, 3 para sair: ”;


cin >> choice;
} // fim do while

cout << “Execução do programa concluída.” << endl;


return 0; // indica terminação bem-sucedida
} // fim de main

78
11. Ponteiros de Função de Função
Ponteiros

void function0( int a ){


cout << “Você entrou ” << a
<< “ então a function0 foi chamada\n\n”;
} // fim da função function0

void function1( int b ){


cout << “Você entrou ” << b
<< “ então a function1 foi chamada\n\n”;
} // fim da função function1

void function2( int c ){


cout << “Você entrou ” << c
<< “ então a function2 foi chamada\n\n”;
} // fim da função function2

79
Função para somar valores a uma determinada variável

Qual valor irá aparecer na tela?

80
Função para somar valores a uma determinada variável

Não reconheceu o valor de 15

81
Função para somar valores a uma determinada variável

82
Função para somar valores a uma determinada variável

Valor correto

83
Função Array

84
Função Array

85
Processamento de string Baseada em Ponteiro

Fundamentos de caracteres e strings baseadas em ponteiro

Um caractere constante é um valor de inteiro (em ASCII) re-


presentado como caractere entre aspas simples.

Uma string é uma série de caracteres tratada como uma uni-


dade única e pode incluir letras, dígitos e vários caracteres es-
peciais como +, -, *, / e $.

Os literais string, ou constantes string, em C++ são escri-


tos em aspas duplas como mostrado a seguir:

Uma string baseada em ponteiro é um array de caracteres


que acabam no caractere nulo (‘\0’), que marca onde a string
termina na memória.
86
Processamento de string Baseada em Ponteiro

87
Processamento de string Baseada em Ponteiro

Um literal string pode ser utilizado como um inicializador na


declaração de um array de caracteres ou de uma variável de
tipo char *.

Para inicializar uma variável como a string “blue”, tem-se:

char color[] = “blue”;


const char *colorPtr = “blue”;

A primeira declaração cria um array color de cinco elementos


contendo os caracteres ‘b’, ‘l’, ‘u’, ‘e’ e ‘\0’; já a segunda cria o
ponteiro de variável colorPtr que aponta para a letra b na
string “blue” (que acaba em ‘\0’) em algum lugar da memória.

88
Processamento de string Baseada em Ponteiro

Os literais string:

• têm a classe de armazenamento static (existem até o fim


do programa);
• são constantes (não podem ser modificados); e
• podem ou não ser compartilhados se o mesmo literal string
for referenciado em um programa.

Uma string pode ser lida em um array de caracteres utilizando


a extração de fluxo com cin.

Por exemplo, a seguinte instrução pode ser utilizada a fim de


ler uma string para o array de caracteres word[ 20 ]:

cin >> word;


89
Processamento de string Baseada em Ponteiro

Esta instrução lê caracteres até que um caractere de espaço


em branco ou indicador de fim do arquivo seja encontrado.

O manipulador de fluxo setw pode ser usado para assegurar


que a string lida em word não exceda o tamanho do array.

A instrução: cin >> setw( 20 ) >> word;

especifica que cin deve ler um máximo de 19 caracteres no


array word e salvar a 20a posição no array para armazenar o
caractere de terminação nulo para a string.

Se mais de 19 caracteres forem inseridos, os caracteres


restantes não são salvos em word, mas serão lidos e podem
ser armazenados em outra variável.
90
Processamento de string Baseada em Ponteiro

Para inserir uma linha inteira de texto em um array, o C++ for-


nece a função cin.getline no cabeçalho <iostream>.

A função cin.getline aceita três argumentos: um array de


caracteres em que a linha de texto será armazenada, um
comprimento e um caractere delimitador.

Por exemplo, o segmento de programa

char sentence[ 80 ];
cin.getline( sentence, 80, ‘\n’ );
\\ ou cin.getline( sentence, 80 );, pois ‘\n’ é tido como padrão

declara o array sentence de 80 caracteres e lê uma linha de


texto do teclado no array.
91
Processamento de string Baseada em Ponteiro

Manipulação de strings da biblioteca de tratamento de strings

A biblioteca de tratamento de strings (cstring) fornece muitas


funções úteis para manipular dados de strings, permitindo:

• comparar strings;
• pesquisar por caracteres e outras strings em strings;
• tokenizar strings (separá-las em partes lógicas como as
palavras separadas em uma frase); e
• determinar seu comprimento.

As funções são resumidas na figura seguinte, sendo que, em


seguida, cada uma delas é utilizada em um exemplo de
‘código ativo’ (live-code).

92
Processamento de string Baseada em Ponteiro

93
Processamento de string Baseada em Ponteiro

// Utilizando strcpy e strncpy.


#include <iostream>
using std::cout;
using std::endl;

#include <cstring> // protótipos para strcpy e strncpy


using std::strcpy;
using std::strncpy;

int main()
{
char x[] = “Happy Birthday to You”; // comprimento: 21
char y[ 25 ];
char z[ 15 ];

94
Processamento de string Baseada em Ponteiro

strcpy( y, x ); // copia conteúdo de x para y

cout << “O string no array x é: ” << x


<< “\nO string no array y é: ” << y << ‘\n’;

// copia os primeiros 14 caracteres de x para z


strncpy( z, x, 14 ); // não copia o caractere nulo
z[ 14 ] = ‘\0’; // acrescenta ‘\0’ ao conteúdo de z

cout << “O string no array z é: ” << z << endl;


return 0; // indica terminação bem-sucedida
} // fim de main

95
Processamento de string Baseada em Ponteiro

// Utilizando strcat e strncat.


#include <iostream>
using std::cout;
using std::endl;

#include <cstring> // protótipos para strcpy e strncpy


using std::strcat;
using std::strncat;

int main()
{
char s1[ 20 ] = “Happy ”; // comprimento 6
char s2[] = “New Year ”; // comprimento 9
char s3[ 40 ] = “”;

96
Processamento de string Baseada em Ponteiro

cout << “s1 = ” << s1 << “\ns2 = ” << s2;

strcat( s1, s2 ); // concatena s2 com s1 (comprimento 15)


cout << “\n\nDepois de strcat(s1, s2):\ns1 = ” << s1
<< “\ns2 = ” << s2;
// concatena os 6 primeiros caracteres de s1 a s3
strncat( s3, s1, 6 ); // coloca ‘\0’ depois de último caractere
cout << “\n\nDepois de strncat(s3, s1, 6):\ns1 = “ << s1
<< “\ns3 = “ << s3;

strcat( s3, s1 ); // concatena s1 a s3


cout << “\n\nAfter strcat(s3, s1):\ns1 = ” << s1
<< “\ns3 = ” << s3 << endl;
return 0; // indica terminação bem-sucedida
} // fim de main
97
Processamento de string Baseada em Ponteiro

// Utilizando strcmp e strncmp.


#include <iostream>
using std::cout;
using std::endl;

#include <cstring> // protótipos para strcpy e strncpy


using std::strcmp;
using std::strncmp;

int main()
{
char *s1 = “Happy New Year”;
char *s2 = “Happy New Year”;
char *s3 = “Happy Holidays”;

98
Processamento de string Baseada em Ponteiro

cout << “s1 = ” << s1 << “\ns2 = ” << s2 << “\ns3 = ”


<< s3 << “\n\nstrcmp(s1, s2) = ” << setw( 2 )
<< strcmp( s1, s2 ) << “\nstrcmp(s1, s3) = ”
<< setw( 2 ) << strcmp( s1, s3 )
<< “\nstrcmp(s3, s1) = ” << setw( 2 )
<< strcmp( s3, s1 );

cout << “\n\nstrncmp(s1, s3, 6) = ” << setw( 2 )


<< strncmp( s1, s3, 6 ) << “\nstrncmp(s1, s3, 7) = ”
<< setw( 2 ) << strncmp( s1, s3, 7 )
<< “\nstrncmp(s3, s1, 7) = ” << setw( 2 )
<< strncmp( s3, s1, 7 ) << endl;
return 0; // indica terminação bem-sucedida
} // fim de main

99
Processamento de string Baseada em Ponteiro

s1 = Happy New Year Com alguns compiladores, strcmp e


s2 = Happy New Year strncmp sempre retornam -1, 0 ou 1.
s3 = Happy Holidays
Com outros, essas funções retornam 0
strcmp(s1, s2) = 0 ou a diferença entre os códigos numéri-
strcmp(s1, s3) = 1 cos dos primeiros caracteres que dife-
strcmp(s3, s1) = -1 rem nas strings sendo comparadas.

strncmp(s1, s3, 6) = 0 Por exemplo, quando s1 e s3 são com-


strncmp(s1, s3, 7) = 1 parados, os primeiros caracteres que
strncmp(s3, s1, 7) = -1 diferem entre eles são os primeiros ca-

racteres da segunda palavra em cada string – N (código 78)


em s1 e H (código 72) em s3 e, nesse caso, o valor de retorno
será 6 (ou -6 se s3 for comparado com s1).
100
Processamento de string Baseada em Ponteiro

// Utilizando strtok.
#include <iostream>
using std::cout;
using std::endl;

#include <cstring> // protótipos para strcpy e strncpy


using std::strtok;

int main()
{
char sentence[] = “This is a sentence with 7 tokens”;
char *tokenPtr;

cout << “The string to be tokenized is:\n” << sentence


<< “\n\nThe tokens are:\n\n”;
101
Processamento de string Baseada em Ponteiro

// inicia a tokenização da frase


tokenPtr = strtok( sentence, “ ” );

// continua tokenizando a frase até tokenPtr tornar-se NULL


while ( tokenPtr != NULL )
{
cout << tokenPtr << ‘\n’;
tokenPtr = strtok( NULL, “ ” ); // obtém o próximo token
} // fim do while

cout << “\nDepois de strtok, sentence = ” << sentence


<< endl;
return 0; // indica terminação bem-sucedida
} // fim de main

102
Processamento de string Baseada em Ponteiro

The string to be tokenized is: Observe que strtok modifica a


This is a sentence with 7 tokens string de entrada; portanto,
uma cópia da string deve ser
The tokens are: feita se o programa exigir a
original depois das chamadas
This para strtok.
is
a O argumento NULL indica que
sentence a chamada a strtok deve
with continuar tokenizando a partir
7 da localização em sentence
Tokens salva pela última chamada a
strtok.
After strtok, sentence = This

103
Processamento de string Baseada em Ponteiro

// Utilizando strlen.
#include <iostream>
using std::cout;
using std::endl;

#include <cstring> // protótipos para strcpy e strncpy


using std::strlen;

int main()
{
char *string1 = “abcdefghijklmnopqrstuvwxyz”;
char *string2 = “four”;
char *string3 = “Boston”;

104
Processamento de string Baseada em Ponteiro

cout << “The length of \”” << string1 << “\” is ”


<< strlen( string1 ) << “\nThe length of \””
<< string2 << “\” is ” << strlen( string2 )
<< “\nThe length of \”” << string3 << “\” is ”
<< strlen( string3 ) << endl;
return 0; // indica terminação bem-sucedida
} // fim de main

The length of “abcdefghijklmnopqrstuvwxyz” is 26


The length of “four” is 4
The length of “Boston” is 6

O caractere de terminação nulo não é incluído no comprimen-


to, sendo que o comprimento também é o índice do caractere
nulo.
105
Structs e ponteiros
Cada registro tem um endereço na memória do computador. (Você pode
imaginar que o endereço de um registro é o endereço de seu primeiro
campo.) É muito comum usar um ponteiro para guardar o endereço de um
registro. Dizemos que um tal ponteiro aponta para o registro. Por exemplo,

data *p; // p é um ponteiro para registros dma


data x;
p = &x; // agora p aponta para x
(*p).dia = 31; // mesmo efeito que x.dia = 31
(Cuidado! Graças às regras de precedência, a expressão *p.dia
equivale a *(p.dia) e tem significado muito diferente de (*p).dia .)
A expressão p->mes é uma abreviatura muito útil para a expressão
(*p).mes :
p->dia = 31; // mesmo efeito que (*p).dia = 31
106
Structs e ponteiros
#include <iostream> int main ()
#include <cstdlib> {
#include <string> data X1;
data *x2;
using namespace std; x2 = &X1;
x2->dia=15;
typedef struct dma { x2->mes=12;
int dia,mes,ano; x2->ano=2025;
}data;
//(*x2).dia=15;
//(*x2).mes=12;
//(*x2).ano=2025;
cout << "A data eh: "<<endl;
cout <<X1.dia;
cout << "/"<<X1.mes;
cout << "/"<<X1.ano<<endl;

system("pause");
return 0;

} 107
Questões
04 - Faça um programa que leia 2 valores inteiros e chame uma
função que receba estas 2 variáveis e troque o seu conteúdo, ou
seja, esta função é chamada passando duas variáveis A e B por
exemplo e, após a execução da função, A conterá o valor de B e B
terá o valor de A.

05 - Faça um programa que leia dois valores inteiros e chame uma


função que receba estes 2 valores de entrada e retorne o maior
valor na primeira variável e o menor valor na segunda variável.
Escreva o conteúdo das 2 variáveis na tela.

06 - Elaborar um programa que leia dois valores inteiros (A e B).


Em seguida faça uma função que retorne a soma do dobro dos
dois números lidos. A função deverá armazenar o dobro de A na
própria variável A e o dobro de B na própria variável B.
108
Questões
07- Crie um programa que contenha uma função que permita
passar por parâmetro dois números inteiros A e B. A função deverá
calcular a soma entre estes dois números e armazenar o resultado
na variável A. Esta função não deverá possuir retorno, mas deverá
modificar o valor do primeiro parâmetro. Imprima os valores de A e
B na função principal.

08- Crie um programa que contenha um array de inteiros contendo


5 elementos. Utilizando apenas aritmética de ponteiros, leia esse
array do teclado e imprima o dobro de cada valor lido.

109
Bibliografia:
● DEITEL, H. M.; DEITEL, P. J. C++:Como Programar. 3. Ed.
Porto Alegre (RS): Bookman, 2001. 1098p
● SOUZA, M. A. F., GOMES, M. M.,SOARES, M. V., CONCILIO,
R., Algoritmos e Lógica de Programação, Editora Cengage
Learning, São Paulo, 2008.

Sergio Barbosa Villas-Boas, C / C++ e Orientação a Objetos
em Ambiente Multiplataforma

Apostila LINGUAGEM C/C++ UNIPAN - Faculdade de Ciências
Aplicadas de Cascavel – FACIAP Curso de Ciência da
Computação, Cascavel - PR, 2004

110

Você também pode gostar