85
Introdução ao uso dos ponteiros - Endereços de memória Desde o início de nosso curso de C, usamos os mais diversos tipos de dados, como int para números inteiros, float e double para números decimais, char para caracteres etc. Vamos agora apresentar um novo tipo dado: os ponteiros. O que são ponteiros em C ? Não se assuste com o tanto de tipos de variáveis, elas são feitas, em sua maioria, para que os humanos possam entender e trabalhar mais facilmente com os diversos tipos de dados. Porém, para o computador, não existe praticamente diferença alguma entre as variáveis, para ele é tudo bit, é tudo 1 ou 0. Um meio bastante usado nos hardwares para administrar esse número gigantesco de 1’s e 0’s, é através do endereçamento. Cada trecho da memória tem um endereço único. Não existem dois bits, em uma máquina, que tenha o mesmo endereço de memória. O ato de selecionar, ou alocar, um espaço de memória em C é feito no momento da declaração. Endereços de memória são um tipo de dado tão importante, ou até mais, que qualquer outro tipo de dado. Quem já trabalhou com eletrônica, em baixo nível, sabe que endereçamento é uma parte essencial de qualquer circuito digital. Nossos computadores possuem diversos dispositivos que são responsáveis somente pelo endereçamento. E é isso que o um ponteiro é: um tipo de dado que serve para indicar, ou armazenar, um endereço de memória. Um ponteiro não é um inteiro, é um tipo que armazena o endereço em que o inteiro está alocado. Um ponteiro não é um float ou double, ponteiro é um tipo de dado que armazena o endereço em que o float ou double está. Um ponteiro não é um char, ponteiro é um tipo de dado que pode armazenar o endereço em que um caractere está.

Ponte Iros

Embed Size (px)

Citation preview

Page 1: Ponte Iros

Introdução ao uso dos ponteiros - Endereços de memóriaDesde o início de nosso curso de C, usamos os mais diversos tipos de dados, como int para números inteiros, float e double para números decimais, char para caracteres etc.

Vamos agora apresentar um novo tipo dado: os ponteiros.

O que são ponteiros em C ?Não se assuste com o tanto de tipos de variáveis, elas são feitas, em sua maioria, para que os humanos possam entender e trabalhar mais facilmente com os diversos tipos de dados.

Porém, para o computador, não existe praticamente diferença alguma entre as variáveis, para ele é tudo bit, é tudo 1 ou 0.Um meio bastante usado nos hardwares para administrar esse número gigantesco de 1’s e 0’s, é através do endereçamento.

Cada trecho da memória tem um endereço único. Não existem dois bits, em uma máquina, que tenha o mesmo endereço de memória.O ato de selecionar, ou alocar, um espaço de memória em C é feito no momento da declaração.

Endereços de memória são um tipo de dado tão importante, ou até mais, que qualquer outro tipo de dado. Quem já trabalhou com eletrônica, em baixo nível, sabe que endereçamento é uma parte essencial de qualquer circuito digital. Nossos computadores possuem diversos dispositivos que são responsáveis somente pelo endereçamento.

E é isso que o um ponteiro é: um tipo de dado que serve para indicar, ou armazenar, um endereço de memória.

Um ponteiro não é um inteiro, é um tipo que armazena o endereço em que o inteiro está alocado.Um ponteiro não é um float ou double, ponteiro é um tipo de dado que armazena o endereço em que o float ou double está.Um ponteiro não é um char, ponteiro é um tipo de dado que pode armazenar o endereço em que um caractere está.

É muito importante que você entenda, e se lembre bem disso, pois é muito comum os iniciantes confundirem ponteiros com outros tipos de dados.

Essa confusão é uma dificuldade natural que todas as pessoas têm ao se depararem pela primeira vez com o uso dos ponteiros.O interessante é que as pessoas não confundem inteiro com um caractere, ou caractere com um número decimal, mas confundem ponteiro com números inteiros.

Isso se deve ao fato dos ponteiros serem um tipo de abstração, criado especialmente para facilitar o trabalho da computação em baixo nível, da computação que mexe diretamente com a memória de seu computador, poder este que pouquíssimas linguagens possuem.

Page 2: Ponte Iros

Como saber o endereço de memória de uma variável: &Sempre que declaramos uma variável e usamos ela, estamos trabalhando com seu valor.

Por exemplo:numero1 = 1;numero2 = 2;letra1 = ‘a’;letra2 = ‘b’;

O valor da variável ‘numero1’ é 1, o valor da variável ‘numero2’ é 2, o valor da variável ‘letra1’ é ‘a’ e o valor da variável ‘letra2’ é ‘b’.Fixe bem esse detalhe: esse é o valor que está armazenado na memória, essas variáveis são um conjunto de bits, um conjunto de informações, um valor.

Agora vamos descobrir em qual posição da memória esses valores estão.Para isso, basta colocarmos o símbolo de E comercial antes da variável: &

Para saber o endereço da variável ‘numero1’, fazemos: &numero1Para saber o endereço da variável ‘numero2’, fazemos: &numero2Para saber o endereço da variável ‘letra1’, fazemos: &letra1Para saber o endereço da variável ‘letra2’, fazemos: &letra2

Para facilitar a visualização do usuário, podemos imaginar a memória como um vetor gigantesco de espaços, e esses espaços são numerados com números inteiros.

Veja bem, embora seja um inteiro, não quer dizer que o valor seja inteiro.Todos os endereços são números inteiros, mas nem todo o valor armazenado dentro daquele endereço de memória é inteiro.

Vamos fazer um exemplo para entender melhor a diferença entre valor e endereço de uma memória.

Exemplo de código: Vendo o valor e endereço de uma variávelCrie um programa em C que declara dois números inteiros e dois caracteres do tipo char (todos devidamente inicializados).Em seguida, mostre o VALOR de cada variável, bem como seu ENDEREÇODepois, altere os valores das variáveis e mostre novamente o VALOR e ENDEREÇO de cada variável desta.

Após rodar esse exemplo, você verá a clara diferença entre o VALOR e o ENDEREÇO de uma variável na memória de seu computador.O valor é aquela informação que você inicia, e endereço é um número inteiro ENORME.

O valor é aquela informação que é alterada, já o endereço de uma variável permanece CONSTANTE!

Faz sentido, para você?

Page 3: Ponte Iros

#include <stdio.h>// Curso C Progressivo: www.cprogessivo.net// O melhor curso de C! Online e gratuito !// Artigos, apostilas, tutoriais e vídeo-aulas sobre// a linguagem de programação C ! int main(void)

{ int numero1=1, numero2=2; char letra1='a', letra2='b'; printf("numero1: \n"); printf("Valor: %d\n", numero1); printf("Endereco na memoria: %d\n\n", &numero1); printf("numero2: \n"); printf("Valor: %d\n", numero2); printf("Endereco na memoria: %d\n\n", &numero2); printf("letra1: \n"); printf("Valor: %c\n", letra1); printf("Endereco na memoria: %d\n\n", &letra1); printf("letra2: \n"); printf("Valor: %c\n", letra2); printf("Endereco na memoria: %d\n\n", &letra2); printf("Alterando os valores...\n\n"); numero1=2112; numero2=666; letra1='A'; letra2='B'; printf("numero1: \n"); printf("Valor: %d\n", numero1); printf("Endereco na memoria: %d\n\n", &numero1); printf("numero2: \n"); printf("Valor: %d\n", numero2); printf("Endereco na memoria: %d\n\n", &numero2); printf("letra1: \n"); printf("Valor: %c\n", letra1); printf("Endereco na memoria: %d\n\n", &letra1); printf("letra2: \n"); printf("Valor: %c\n", letra2); printf("Endereco na memoria: %d\n\n", &letra2); return 0; }

Page 4: Ponte Iros

A função sizeof() e os blocos vizinhos de memóriaNo artigo introdutório de nosso curso, demos uma ideia sobre o que são ponteiros em C, além de falarmos sobre os endereços de memória.Agora, nesse artigo, vamos entrar mais a fundo no estudo da memória e ver, de fato, onde as variáveis estão sendo declaradas e um importante relação entre ponteiros e variáveis que ocupam vários bytes. 

Quando cada variável ocupa em memória: A funçãosizeof()Como dissemos, o C vê sua memória RAM como um vetor enorme de bytes, que vão desde o número 0 até o tamanho dela (geralmente alguns Gb).

Sempre que declaramos uma variável em C, estamos guardando, selecionando ou alocando um espaço de bytes desses, e dependendo do tipo de variável, o tanto de memória reservada varia.

Por exemplo, embora as variáveis do tipo float edouble sejam usadas para representar números em sua forma decimal, as variáveis do tipodouble têm, como o próprio nome sugere, o dobro de precisão. Ou seja, podemos colocar muito mais casas decimais em variáveis desse tipo. E para que isso aconteça, é óbvio que vamos precisar de um espaço maior em memória.

Podemos descobrir quantos bytes certa variável ocupa através da função sizeof().Essa função recebe uma variável como argumento, ou as palavras reservadas que representam as variáveis: char, int, float etc.

Como você poderá ver ao longo de nossa apostila de C, a função sizeof() é MUITO importante e será MUITO usada. Ela é tão importante que foi definido um novo 'tipo' de variável para sua saída.Sempre que usamos a função sizeof(), ela retorna variáveis do tipo: size_tLembre-se bem desse tipo. Vamos usar bastante na seção de strings e de alocação dinâmica de memória.

Vamos ver um exemplo que mostra o uso da sizeof.

Exemplo de código: Como saber quantos bytes cada variável ocupa em memóriaFaça um programa em C que mostra quantos bytes ocupam cada uma das variáveis: char, int, float e double.

Existem duas maneiras de fazer isso, a primeira é simplesmente colocando as palavras reservadas dentro da função sizeof(). A segunda maneira é declarando variáveis e colando ela dentro da função sizeof(), como faremos no próximo exemplo.

Page 5: Ponte Iros

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

printf("Char: %d bytes\n", sizeof(char));

printf("Int: %d bytes\n", sizeof(int));

printf("Float: %d bytes\n", sizeof(float));

printf("Double: %d bytes\n", sizeof(double));

return 0;

}

Exemplo: Mostrar o endereço e número de bytes que cada variável ocupaAgora, além de mostrar quantos bytes cada variável ocupa, mostre o endereço dela.Para isso, declare 4 variáveis: uma char, um int, um float e um double.

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

char caractere;

int inteiro;

float Float;

double Double;

printf("Caractere: %d bytes \t em %d\n", sizeof(caractere),

&caractere);

printf("Inteiro: %d bytes \t em %d\n", sizeof(inteiro), &inteiro);

printf("Float: %d bytes \t em %d\n", sizeof(Float), &Float);

printf("Double: %d bytes \t em %d\n", sizeof(Double), &Double);

return 0;

}

Page 6: Ponte Iros

Endereço de um bloco de variáveisAgora que você já viu o tamanho e localização que cada variável ocupa, vamos ensinar um fato importante, que será muito usado em nosso estudo de strings e na compreensão da passagem de parâmetro por referência, em funções.

Quando declaramos uma variável ou vetor de variáveis, os blocos de memória são vizinhos, são separados em fila, numa ordem.

Você viu no exemplo passado que, tirando o tipo char, as outras variáveis ocupam mais de uma posição em memória (lembrando que 1 byte é uma posição, ou bloco de memória).O tipo double, por exemplo, ocupa 8 bytes. Então, esses 8 espaços de memória são alocados de forma contígua (um ao lado do outro).

Sempre que declaramos uma variável que ocupa mais de um bloco de memória, o seu endereço será o endereço do primeiro bloco. Ou seja, se um inteiro ocupa 4 bytes (4 blocos), e usamos o operador & para ver seu endereço, o endereço que vemos é o endereço do primeiro byte. E qual o endereço dos outros blocos? Ué, são vizinhos. Então, ocupam bytes, ou posições de memória vizinha.Se o primeiro byte está em 2112, o segundo vai estar na posição 2113, o terceiro na posição 2114 e o quarto na posição 2115.

Vamos supor que queiramos declarar 1 milhão de inteiros em um vetor:int numerozao[1000000];

O endereço de 'numerozao' é o endereço de 'numerozao[0]', pois o endereço de uma variável ou vetor, é o endereço do primeiro bloco. E quantos bytes essa variável ocupa?1 milhão * 4 bytes. = 4 000 000 bytesTeste com : sizeof(numerozao)

(Por ser um número muito grande de memória, há a possibilidade de sua máquina ficar lenta, de dar erro no programa, ou mesmo sua máquina travar. Lembre-se, a linguagem C é  poderosa, você está manipulando e controlando cada pedacinho de sua máquina, por isso é fácil fazer alguma coisa que possa travar seu sistema. Não é à toa que C é a linguagem favorita dos hackers e a mais usada na criação de vírus).

O mesmo ocorre para um ponteiro. Sabemos que os ponteiros, ou apontadores, armazenam o endereço de apenas um bloco de memória.Quando um ponteiro aponta para uma variável que ocupa vários bytes, adivinha pra qual desses bytes o ponteiro realmente aponta? Ou seja, o endereço que o tipo ponteiro armazena, guarda o endereço de qual byte?Do primeiro. Sempre do primeiro.

Page 7: Ponte Iros

E como ele sabe o endereço dos outros? Os outros estão em posições vizinhas!

Vamos mostrar isso na prática com um exemplo.Para isso, temos que declarar duas variáveis de um mesmo tipo, e ver seus endereços de memória.

Exemplo de código: Mostrando as posições dos blocos de variáveisCrie duas variáveis de cada tipo: char, int, float e double - e mostre o endereço destas.

Ao declarar e ver os endereços, note que as variáveis do mesmo tipo, que foram declaradas juntas, estão em endereços de memória contínuos.

No caso das chars, elas estão ao lado da outra, pois só ocupam 1 byte cada.No caso dos inteiros e floats, o espaço de endereço de uma variável pra outra é de 4 unidades, pois cada variável desta ocupa 4 bytes.

Nas variáveis do tipo double, seus endereços estão distantes em 8 unidades, bytes, um do outro.

Rode e veja:

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

char caractere1, caractere2;

int inteiro1, inteiro2;

float Float1, Float2;

double Double1, Double2;

printf("Caracteres: %d e %d\n", &caractere1, &caractere2);

printf("Inteiros: %d e %d\n", &inteiro1, &inteiro2);

printf("Floats: %d e %d\n", &Float1, &Float2);

printf("Doubles: %d e %d\n", &Double1, &Double2);

return 0;

}

Page 8: Ponte Iros

Como declarar, inicializar e usar ponteiros em C - A constante NULLAgora que já vimos os conceitos teóricos sobre memória, blocos de memória, endereçamento e do uso da função   sizeof() , vamos de fato usar os ponteiros.

Nesse tutorial de C vamos ensinar como declarar ponteiros, fazê-los apontarem para alguma variável ou vetor, e manipulá-los.

Como declarar ponteiros em CPara declarar um ponteiro, ou apontador, em C basta colocarmos um asterisco - * - antes do nome desse ponteiro.

Sintaxe:tipo *nome_do_ponteiro;

Por exemplo:int *ponteiro_pra_inteiro;float *ponteiro_pra_float;char *ponteiro_pra_char;

Na verdade, esse asterisco pode ser encostado no tipo ou entre o tipo e o nome.

Aqui, se você estiver com os conceitos de ponteiro na cabeça, pode surgir uma pergunta.“Se os ponteiros são tipos que armazenam endereço, e endereço são apenas números, por quê ter que declarar ponteiros com os tipos (int, float, char etc) ?”

A resposta é dada no artigo passado, em que falamos sobre o tamanho que as variáveis ocupam em memória.Vimos que as variáveis ocupam posições vizinhas e contíguas (em seqüência) de memória (exceto, claro, o tipo char, que ocupa só 1 byte, ou seja, só um bloco).

Vamos pegar o exemplo da variável inteira. Em minha máquina, ela ocupa 4 bytes.Ou seja, 4 blocos de memória, cada bloco com um endereço.Mas o ponteiro armazena apenas um endereço de memória, e não 4.Então, o ponteiro irá sempre armazenar o endereço do primeiro bloco, do primeiro byte.

E os outros? Ué, se o C sabe quantos bytes cada variável ocupa, que elas são blocos vizinhos de memória e o ponteiro sabe o endereço do primeiro bloco, ele vai saber dos outros também!

É por isso que precisamos dizer o tipo de variável, antes de declarar o ponteiro.

Page 9: Ponte Iros

Se for um ponteiro de inteiro, estamos dizendo: “Ponteiro, guarde esse endereço e os próximos 3, pois o inteiro tem 4 bloco”.Se for um double: “Ponteiro, armazene o primeiro endereço, e saiba que os próximos 7 blocos são dessa mesma variável.”

Ponteiros e Vetores em CJá explicamos sobre a relação dos ponteiros com os diversos tipos de blocos de memória, de cada variável.E a relação dos ponteiros com os vetores, que possuem diversas variáveis?

Pois bem, eles têm (ponteiros e vetores) possuem uma relação especial.Quando declaramos um vetor, estamos declarando um conjunto de variáveis também contíguas, e cada uma dessas variáveis ocupam vários bytes (ou só 1 byte, se for char). Então, um vetor é um conjunto maior ainda de bytes, de blocos de memória.

Como você sabe, quando apontamos um ponteiro para uma variável, esse ponteiro armazena o endereço do primeiro byte, do menor endereço, da variável.A relação com vetores é análoga: o nome do vetor é, na verdade, o endereço do primeiro elemento desse vetor.

Ou seja, se declararmos um vetor de nome vetor, não importando o número de elementos, se imprimirmos o nome vetor dentro de um printf, veremos o endereço da primeira variável daquele vetor.Podemos ver um vetor como um ponteiro.

Isso explica o fato de que quando passamos um vetor para uma função, essa função altera de fato o valor do vetor. Isso ocorre pois não estamos passando uma cópia do vetor (como acontece com as variáveis).Isso ocorre porque quando passamos o nome do vetor, estamos passando um ponteiro pra função.Ou seja, estamos passando um endereço, onde a função vai atuar.E endereço de memória é o mesmo, dentro ou fora de uma função.

Rode o seguinte exemplo para se certificar do que foi ensinado aqui.Exemplo de código: Crie um programa que mostre que o nome de um vetor é, na verdade, um ponteiro para a primeira posição que o vetor ocupa na memória. Ou seja, um vetor sempre aponta para o elemento 0.

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

Page 10: Ponte Iros

// a linguagem de programação C !

int main(void)

{

int teste[10];

printf("Imprimindo o vetor 'teste': %d\n", teste);

printf("Imprimindo o endereço do primeiro elemento: %d\n",

&teste[0]);

return 0;

}

Ou seja, para declararmos um ponteiro ptr para um vetor vet[ ], fazemos:ptr = vet;

Pois o nome do vetor é um ponteiro (que não muda) para o primeiro elemento.Então poderíamos fazer assim também:ptr = &vet[0];

Como inicializar um ponteiro em C – A constanteNULLJá vimos como declarar um ponteiro, então é hora de fazer com que eles cumpram sua missão.Vamos fazer os ponteiros apontarem.

Lembra que ensinamos como checar o endereço de uma variável ou vetor apenas usando o símbolo & antes da variável?Agora vamos fazer isso novamente, mas é para pegar esse endereço e armazenar em um ponteiro.

Por exemplo, se quisermos armazenar o endereço do inteiro ‘numero’ no ponteiro ‘numeroPtr’, fazemos:

int numero = 5;int *numeroPtr = &numero;

Pronto, agora nosso ponteiro está apontando para a variável numero, pois o ponteiro guardou o endereço do inteiro na sua posição de memória.Muito cuidado! Ponteiros armazenam endereços, e não valores. Ou seja, se fizer:int *numeroPtr = numero;

Estará comentendo um erro!

Page 11: Ponte Iros

É sempre bom inicializarmos os ponteiros, pois senão eles podem vir com lixo e você se esquecer, posteriormente, de inicializar. Então, quando for usar, pensará que está usando o ponteiro de modo correto, mas estará usando o ponteiro com ele apontando para um lixo (endereço qualquer de memória).

Uma boa prática é apontar os ponteiros para a primeira posição de memória, que é conhecida como NULL. Sempre que terminar de usar um ponteiro, coloque ele pra apontar para a posição NULL. Para fazer isso, faça:tipo *nome_do_ponteiro = NULL;

Exemplo de código: Como usar ponteirosCrie um programa em C que declara um inteiro e uma variável do tipo double. Em seguida, crie dois ponteiros apontando para essas variáveis e mostre o endereço de memória das variáveis, e mostre o endereço de memória que cada ponteiro armazenou.Por fim, coloque esses ponteiros para a primeira posição (NULL), de memória.

Para saber o endereço de uma variável dentro do printf, colocamos o %d e depois ‘&nome_variavel’.Para saber que endereço um ponteiro armazena no printf, também colocamos o %d entre as aspas, e fora colocamos apenas o nome do ponteiro.

Veja como ficou nosso código sobre como fazer esse programa em C:

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

int inteiro;

int *inteiro_ptr = &inteiro;

double double1;

double *double_ptr = &double1;

printf("Endereco da variavel 'inteiro': %d\n", &inteiro);

printf("Endereco armazenado no ponteiro 'inteiro_ptr': %d\n\n",

inteiro_ptr);

printf("Endereco da variavel 'double1': %d\n", &double1);

Page 12: Ponte Iros

printf("Endereco armazenado no ponteiro 'double_ptr': %d\n\n",

double_ptr);

printf("Apos o uso dos ponteiros, vamos aponta-los para NULL\n\

n");

inteiro_ptr = NULL;

double_ptr = NULL;

printf("Endereco armazenado no ponteiro 'inteiro_ptr': %d\n",

inteiro_ptr);

printf("Endereco armazenado no ponteiro 'double_ptr': %d\n",

double_ptr);

return 0;

}

Variáveis apontadas - A Passagem por Referência em CJá vimos como declarar e inicializar ponteiros em C, bem como estudamos a teoria por trás dos endereços de memória e blocos de memórias, vamos mostrar a maior utilidade dos ponteiros, que é trabalhar com osvalores das variáveis para qual eles apontam, e alterar seu valor.

Obtendo o valor apontado pelo ponteiro: *

Para obtermos o valor da variável na qual o ponteiro aponta, devemos colocar um asterisco - * - antes do ponteiro, assim, o ponteiro irá mostrar o valor da variável (a variável que ele aponta), e não mais seu endereço.

Por exemplo, vamos supor que tenhamos declarado a variável inteira ‘numero’:int numero = 1;

Agora vamos criar um ponteiro ‘ptr_int’ e fazê-lo apontar para ‘numero’:int ptr_int = &numero;

Pronto, agora ptr_int aponta para numero.Para saber o valor de numero através do ponteiro, usamos: *ptr_int

Veja bem:ptr_int   -> armazena o endereço da variável numero*ptr_int -> se refere ao VALOR da variável, ou seja, ao valor da variável numero.

Page 13: Ponte Iros

Exemplo de código: Mostrando o valor das variáveis apontadas por ponteirosCrie um programa em C que peça ao usuário três números inteiros e armazene em três variáveis inteiras através do uso de um ponteiro.Após o usuário inserir cada número, mostre o número exibido, porém mostre através do ponteiro.

Desde o início de nosso curso de C, quando ensinamos como obter dados a partir do usuário, através da função scanf(), nós vínhamos usando o operador & dentro dessa função para que o dado inserido pelo usuário fosse armazenado em determinada posição de memória.

Aqui, vamos criar três inteiros e um ponteiro de inteiro, que irá apontar para cada uma dessas variáveis. Usaremos o ponteiro dentro da scanf(uma vez que ele guarda o endereço da variável pra onde ele aponta) e o asterisco, para mostrar o valor da variável que é apontada por ele.

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

int int1, int2, int3;

int *ptr_int = &int1;

printf("Inteiro 1: ");

scanf("%d", ptr_int);

printf("Numero inserido: %d\n", *ptr_int);

ptr_int = &int2;

printf("Inteiro 2: ");

scanf("%d", ptr_int);

printf("Numero inserido: %d\n", *ptr_int);

ptr_int = &int3;

printf("Inteiro 3: ");

scanf("%d", ptr_int);

printf("Numero inserido: %d\n", *ptr_int);

return 0;

}

Page 14: Ponte Iros

O que é e como fazer a Passagem por referência em CPara passarmos uma variável para uma função e fazer com que ela seja alterada, precisamos passar a referência dessa variável, em vez de seu valor.

Até o momento, vínhamos passando somente o valor das variáveis.Conforme explicamos, quando passamos um valor, a função copia esse valor e trabalhar somente em cima da cópia dessa variável, e não na variável em si. Por isso nossas variáveis nunca eram alteradas quando passadas para funções.

Porém, é muito importante e útil que algumas funções  alterem valores de variáveis.Para fazer isso, usaremos um artifício chamado Passagem por Referência.Por referência entenda endereço, um local. No caso, em vez de passar o valor da variável, na passagem por referência vamos passar o endereço da variável para a função.

Para fazer isso, basta colocar o operador & antes do argumento que vamos enviar, e colocar um asterisco * no parâmetro da função, no seu cabeçalho de declaração, para dizer a função que ela deve esperar um endereço de memória, e não um valor.

Sim, parece confuso e difícil entender assim de cara, mas vamos mostrar dois exemplos em que usaremos a passagem por referência.

Exemplo de código: Passagem por referência em CCrie um programa que recebe um inteiro e dobra seu valor.

Para que uma função altere o valor de uma variável, é necessário que essa função atue no endereço de memória, e não na copilar do valor. Para isso, temos que passar o endereço da variável para a função (usando &), e a função tem que ser declarada de modo a esperar um ponteiro (usando *).

Dentro da função, devemos trabalhar com o valor na qual aquele ponteiro aponta.Ou seja, se o ponteiro tiver nome ‘ptr’, devemos trabalhar com o valor ‘*ptr’.

Veja como ficou nosso código C:

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

void dobra(int *num)

Page 15: Ponte Iros

{

(*num) = (*num) * 2;

}

int main(void)

{

int num;

printf("Insira um numero: ");

scanf("%d", &num);

dobra(&num);

printf("O dobro dele eh: %d\n", num);

return 0;

}

Exemplo de código: Trocar o valor de dois números em CCrie um programa em C que peça ao usuário o valor de uma variável x e depois de uma y.Crie uma função que inverta esses valores através do uso de ponteiros.

Mais uma vez, declaramos uma função que irá receber dois ENDEREÇOS de memória, ou seja, irá receber dois ponteiros. Esses ponteiros têm os locais onde as variáveis x e y foram armazenadas.Para alterar esses valores, vamos trabalhar com (*x) e (*y).

Para fazer duas variáveis trocarem de valores entre si, iremos precisar de uma variável temporária.Primeiro guardamos o valor de x na variável temporária (vamos precisar depois).Em seguida, fazemos x receber o valor de y.Agora é y que tem que receber o valor de x. Mas x mudou de valor, lembra?Ou seja, precisamos pegar o antigo valor de x. Foi por isso que guardamos esse antigo valor de x na variável temporária.Logo, agora é só fazer com que y pegue o valor da variável temporária, e teremos os valores invertidos.

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

void troca(int *x, int *y)

Page 16: Ponte Iros

{

int tmp;

tmp = *x;

*x = *y;

*y = tmp;

}

int main(void)

{

int x, y;

printf("Insira o numero x: ");

scanf("%d", &x);

printf("Insira o numero y: ");

scanf("%d", &y);

troca(&x, &y);

printf("Agora x=%d e y=%d\n", x, y);

return 0;

}

Operações Matemáticas com Ponteiros em C

Agora que ensinamos como fazer os ponteiros apontarem para variáveis, bem como ver e alterar o valor de variáveis através dos ponteiros, vamos dar mais exemplos reais de uso dos ponteiros, com operações de incremento, decremento, comparação de igualdade e negação.

Incremento,  Decremento e Comparando ponteiros em CUma das maiores utilidades dos ponteiros é fazer com que eles apontem para quantas variáveis desejarmos.Por exemplo, podemos fazer com que um únicoponteiro percorra todas as variáveis de um vetor.

Page 17: Ponte Iros

E através do operador asterisco, além de percorrer as variáveis, também podemos fazer operações com elas.

A vantagem de usarmos ponteiros para percorrer e alterar variáveis é a eficiência, pois o C trabalha bem mais rápido se usarmos endereços de memória (ponteiros), ao invés de ficarmos declarando e alterando valores de variáveis.

Além disso, o uso de ponteiros para o estudo de Arquivos, Alocação Dinâmica de Memória e de Strings, na próxima seção, é essencial em nosso curso de C.

Assim  como outras variáveis, podemos, por exemplo, incrementar e decrementar ponteiros.Como os ponteiros são variáveis para armazenar endereços de memória, se fizermos operações de incremento e decremento em ponteiros, é o endereço que irá mudar.

Porém, quando incrementamos ou decrementamos um ponteiro, seu endereço não percorre os endereços em uma unidade, ele percorre em sizeof(tipo_variável).

Por exemplo, sabemos que o tipo double ocupa 8 bytes em memória.Se tivermos um ponteiro para um vetor tipo de variável, ele guarda a primeira posição do vetor.Vamos supor que esse endereço seja: 2112Quando incrementamos o ponteiro: ptr++;O valor que ele aponta não será o pro endereço 2113, e sim pro endereço 2120, pois a próxima variável do vetor vai estar na posição 2120, já que cada variável ocupa 8 bytes e a primeira estava na posição 2112.

Ou seja, quando incrementamos ou decrementamos um ponteiro, que aponta para um tipo, ele vai percorrer o número de bytes do tamanho de seu tipo.Se for char, o ponteiro muda sua posição de 1 byte para frente ou para trás no endereço.Se for int, o ponteiro muda sua posição  em 4 bytes, para frente ou para trás no endereço.

O mesmo vale para outras operações matemáticas, como soma e subtração:ptr + 1 -> endereço de onde ptr apontava + sizeof(tipo).ptr + 2 -> endereço de onde ptr apontava, deslocado de duas variáveis para frente.ptr – 3 -> endereço de onde ptr apontava, deslocado de duvas variáveis para trás

Exemplo de código: Calcular os termos de uma P.A usando Ponteiros em CPeça ao usuário dois inteiros, onde o primeiro será o primeiro termo de uma P.A (Progressão Aritmética) e o segundo a razão. Em segundo, através de ponteiros, preencha um vetor com os 10 primeiros elementos dessa P.A.

Page 18: Ponte Iros

Após declararmos um vetor de 10 elementos, fazemos com que o ponteiro aponte para esse vetor. Como o nome vetor nada mais é que do que o endereço do primeiro elemento.

Assim, inicialmente, nosso ponteiro aponta para o primeiro elemento do vetor, que será o primeiro elemento da P.A.

Para calcular os elementos da P.A, basta fazermos com que o próximo termo seja o atual mais a razão:*(ptr+1) = *ptr + razao

Entramos no laço WHILE que irá percorrer todos os elementos do vetor, exceto o último (pois o último elemento é calculado pelo penúltimo, então não precisa ir até o último).

Ou seja, só irá parar depois de termos atingidos o penúltimo elemento do vetor, que é pa[8].Como vamos usar somente ponteiros para percorrer o vetor, nosso WHILE deve continuar enquanto o endereço de nosso ponteiro for diferente último endereço do vetor (que não precisará ser acessado, pois cada iteração do looping calcula o próximo termo da PA).

Em seguida, usamos o laço FOR para você ver como podemos manipular ponteiros dentre dele também.Inicializamos, novamente, o ponteiro a partir do primeiro elemento: ptr = pa;Depois, imprimimos todos os elementos, enquanto o endereço que o ponteiro aponta for menor ou igual ao último endereço do vetor.

Veja como ficou nosso código:

#include <stdio.h>

// Curso C Progressivo: www.cprogessivo.net

// O melhor curso de C! Online e gratuito !

// Artigos, apostilas, tutoriais e vídeo-aulas sobre

// a linguagem de programação C !

int main(void)

{

int pa[10], razao;

int *ptr;

printf("Insira o termo inicial da PA: ");

scanf("%d", &pa[0]);

Page 19: Ponte Iros

ptr = pa;

printf("Insira razao da PA: ");

scanf("%d", &razao);

while( ptr != &pa[9])

{

*(ptr+1) = *ptr + razao;

ptr++;

}

for(ptr = pa ; ptr <= &pa[9] ; ptr++)

printf("-> %d ", *ptr);

return 0;

}

Apostila UFMG: Questões sobre ponteiros com gabarito

O seguinte material era um curso ofertado pela UFMG (Universidade Federal de Minas Gerais) até 2005.Devido a qualidade e grande uso da apostila, até hoje eles deixam seu material para quem desejar estudar, consultar ou divulgar.

Indicamos, pois a apostila é uma excelente fonte de estudo, e como programador C você estudar o máximo possível por diversas fontes.Apostila de linguagem C da UFMG

O seguinte material é uma coletânea de questões sobre ponteiros, assunto que já foi ensinado em nosso curso C Progressivo:Ponteiros (ou apontadores) em C--

  Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questão. Ao terminar, aperte o botão "Verifica  nota" no final do formulário. Você pode fazer quantas tentativas quiser até atingir a nota 100. Se quiser recomeçar, basta apertar o botão "Limpa" no final do formulário. 

1- Seja um vetor declarado por 

Page 20: Ponte Iros

        int vet[10]; 

    Qual elemento deste vetor é acessado quando se escreve vet[2] ?    

a. Primeiro elemento 

b. Segundo elemento 

c. Terceiro elemento 

d. Quarto elemento 

e. Nenhuma das opções anteriores 

 2- Se declararmos um vetor como:     int vet[30] 

    a instrução abaixo acessa corretamente os elementos deste vetor?     for (j=0; j <= 30; j++)       vet[j] = j*j; 

   

a. Sim

b. Não 

 3- Seja a matriz matrx declarada e inicializada por:     int matrx[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; 

O que conterá o elemento matrx[1][2] ?    

a. 2

b. 5 

c. 6 

d. 7 

e. Nenhuma das opções anteriores 

 4- Se uma string for declarada como:     char str[20]; o número máximo de caracteres que poderão ser lidos e armazenados nela é: 

   

a. 18 

b. 19 

c. 20

Page 21: Ponte Iros

d. 21 

 5- Qual função pode ser usada para determinar o comprimento de uma string? 

   

a. gets

b. strcpy

c. strcat

d. strlen

e. strcmp

 6- Qual das instruções abaixo é correta para declarar um ponteiro para inteiro? 

   

a. *int pti; 

b. *pti;

c. &i; 

d. int_pti pti; 

e. int *pti; 

 7-  Seja a seguinte seqüência de instruções em um programa C:     int *pti;     int i = 10;     pti = &i; 

    Qual afirmativa é falsa?    

a.  pti armazena o endereço de i 

b. *pti é igual a 10 

c. ao se executar *pti = 20; i passará a ter o valor 20

d. ao se alterar o valor de i, *pti será modificado

e. pti é igual a 10

 8- Se i e j são variáveis inteiras e pi e pj são ponteiros para inteiro, qual atribuição é ilegal? 

   

Page 22: Ponte Iros

a. pi = &i; 

b. *pj = &j; 

c. pj = &*&j; 

d. i = *&*&j; 

e. i = (*pi)+++*pj; 

 9- Seja a seguinte seqüência de instruções em um programa C:         int *pti;         int veti[]={10,7,2,6,3};         pti = veti; 

Qual afirmativa é falsa?    

a. *pti é igual a 10 

b. *(pti+2) é igual a 2 

c. pti[4] é igual a 3

d. pti[1] é igual a 10 

e. *(veti+3) é igual a 6

 10- Na seqüência de instruções abaixo:     float f;     float *pf;     pf = &f;     scanf("%f", pf); 

   

a. Efetuamos a leitura de f

b. Não efetuamos a leitura de f

c. Temos um erro de sintaxe 

d. Deveríamos estar usando &pf no scanf 

e. Nenhuma das opções anteriores 

 11- Seja a seguinte seqüência de instruções     int i=10, j=20;     int *pti, *ptj; 

    pti = &i;     ptj = &j; 

Qual expressão não é válida?  

Page 23: Ponte Iros

a. j = pti == ptj;

b. i = pti-ptj; 

c. pti += ptj;

d. pti++; 

e. i = pti || ptj; 

 12- Seja a declaração:     int matr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12} 

Qual afirmativa é falsa?  

a. **matr é igual a 1

b. *(*(matr+1)+2) é igual a 7 

c. *(matr[2]+3) é igual a 12

d. (*(matr+2))[2] é igual a 11

e. *((*matr)+1) é igual a 5

 Sua nota é:  em um máximo de 100. 

Ponteiros

Em Construção, com vi!!! copyright © Adriano Cruz 1999

Page 24: Ponte Iros

1. Introdução 2. Operações com Ponteiros

1. Declaração de Ponteiros

2. Os Operadores de Ponteiros

3. Aritmética de Ponteiros

3. Ponteiros e Vetores

1. Ponteiros e Strings

2. Alocação Dinâmica de Memória

3. Ponteiros e Matrizes

4. Vetores de Ponteiros

5. Ponteiros para Ponteiros

4. Exercícios

Introdução

Ponteiros são usados em situações em que é necessário conhecer o endereço onde está armazenada a variável e não o seu conteúdo.

Um ponteiro é uma variável que contém um endereço de memória e não o conteúdo da posição.

 A memória de um computador pode ser vista como uma sequência de bytes cada um com seu próprio endereço. Não há dois bytes com o mesmo endereço. O primeiro endereço é sempre 0 e o último geralmente é uma potência de 2. Por exemplo um computador com memória igual a 16 Mbytes tem 16x1024x1024 bytes.

A figura abaixo mostra um mapa de um trecho de memória que contém duas variáveis (num, res) inteiras de tipo longo (4 bytes cada uma). Observar que os endereços estão pulando de quatro em quatro já que as variáveis são inteiras de tipo longo. Uma possível declaração destas variáveis dentro de um programa Cpoderia ser:

long int  num=10, res=120;Endereços  Conteúdo  Variável 

Page 25: Ponte Iros

996  ---  --- 1000  10  num 1004  120  res

Tabela 8.1 Mapa de Memória

Ponteiros são importantes por exemplo quando se deseja que uma função retorne mais de um valor.

Por exemplo, uma função pode receber não os valores dos parâmetros mas sim ponteiros que apontem para seus endereços. Assim esta função pode modificar diretamente os conteúdos destas variáveis.

 Uma outra aplicação importante de ponteiros é apontar para áreas de memória que são admnistradas durante a execução do programa. Com ponteiros é possível alocar as posições de memória necessárias para armazenamento de vetores somente quando o programa estiver rodando. O programador pode reservar o número exato de posições que o programa requer.

Operações com Ponteiros

Declaração de Ponteiros

Antes de serem usados os ponteiros, como as variáveis, precisam ser declarados. A forma geral da declaração de um ponteiro é a seguinte:    tipo *nome;

Onde tipo é qualquer tipo válido em C e nome é o nome da variável ponteiro. Por exemplo:int *res;       /* ponteiro para uma variavel inteira */float *div;     /* ponteiro para uma variavel de ponto flutuante */

Os Operadores de Ponteiros

Existem dois operadores especiais para ponteiros: * e &. Os dois operadores são unários, isto é requerem somente um operando.

 O operador & devolve o endereço de memória do seu operando. Por exemplo,

pint = &soma;   /* o endereco de soma e carregado em pint */

Page 26: Ponte Iros

No exemplo seguinte considere a Tabela 8.1. Após a execução do trecho de programa abaixo a variável ponteiro p termina com o valor 1000.p = &num;

O operador * é o complemento de &. O operador * devolve o valor da variável localizada no endereço que o segue. Por exemplo, o comandonum = *p;

significa que a variável num recebe o valor apontado por p. Estes operadores não devem ser confundidos com os já estudados em capítulos anteriores. O operador * para ponteiros não tem nada a ver com o operador multiplicação. O operador ponteiro * é unário e, como o operador &, tem precedência maior que do que todos os operadores aritméticos. 

Aritmética de Ponteiros

Atribuição de Ponteiros

 Do mesmo modo que uma variável comum o conteúdo de um ponteiro pode ser passado para outro ponteiro do mesmo tipo. As variáveis ponteiro devem sempre apontar para os tipos de dados corretos. Uma variável ponteiro declarada como apontador de dados inteiros deve sempre apontar para dados deste tipo.

Observar que em C é possível atribuir qualquer endereço a uma variável ponteiro. Deste modo é possível atribuir o endereço de uma variável do tipo float a um ponteiro inteiro. No entanto, o programa não irá funcionar da maneira correta.

 Por exemplo, no trecho de programa abaixo c8atribp.c o endereço do terceiro elemento do vetor v é carregado em p1 e o endereço da variável i é carregado emp2. Além disso no final o endereço apontado por p1 é carregado em p2. Os comandos printf imprimem os valores apontados pelos ponteiros respectivos.    

void main(void){    int vetor[] = { 10, 20, 30, 40, 50 };    int *p1, *p2;    int i = 100;

    p1 = &vetor[2];    printf("%d\n", *p1);    p2 = &i;    printf("%d\n", *p2);    p2 = p1;    printf("%d\n", *p2);

Page 27: Ponte Iros

}

Acessando os Endereços

 O trecho de programa abaixo faz com que o endereço da variável x seja carregado no ponteiro p. Em seguida o programa imprime o que está apontado por p.

void main(void){   float x=3.14; float *p;

   p = &x;    printf("*p = %f", *p);}

Incrementando e Decrementando Ponteiros

 O exemplo (c8incrp.c) abaixo mostra que operações de incremento e decremento podem ser aplicadas em operandos. O primeiro printf imprime 30 o segundo 40 e o terceiro 50.

void main(void){    int vetor[] = { 10, 20, 30, 40, 50 };    int *p1;        p1 = &vetor[2];    printf("%d\n", *p1);    p1++;    printf("%d\n", *p1);    p1 = p1 + 1;    printf("%d\n", *p1);}

Pode parecer estranho que um endereço que aponte para um número inteiro que é armazenado em dois bytes seja incrementado por um e passe para apontar para o próximo número inteiro. A resposta para isto é que sempre que um ponteiro é incrementado (decrementado) ele passa a apontar para a posição do elemento seguinte (anterior). Do mesmo modo somar três a um ponteiro faz com que ele passe apontar para o terceiro elemento após o atual. Portanto, um incremento em um ponteiro que aponta para um valor que é armazenado em n bytes faz que n seja somado ao endereço.

 É possível se usar o seguinte comando

*(p+1)=10;

Este comando armazena o valor 10 na posição seguinte àquela apontada por p. É possível somar-se e subtrair-se inteiros de ponteiros. A operação abaixo faz com que o ponteiro p passe a apontar para o terceiro elemento após o atual.p = p + 3;

Page 28: Ponte Iros

A diferença entre ponteiros fornece quantos elementos do tipo do ponteiro existem entre os dois ponteiros. No exemplo (c8difp.c) abaixo é impresso o valor 3.void main(void){        float vetor[] = { 1.0, 2.0, 3.0, 4.0, 5.0 };        float *p1, *p2;            p1 = &vetor[2];         /* endereco do terceiro elemento */        p2 = &vetor;            /* endereco do primeiro elemento */    printf("Diferenca entre ponteiros %d\n", p1-p2);}

Não é possível multiplicar ou dividir ponteiros, e não se pode adicionar ou subtrair o tipo float ou o tipo double a ponteiros.

Comparação de Ponteiros

 É possível comparar ponteiros em uma expressão relacional. Só é possível comparar ponteiros de mesmo tipo. O trecho de programa abaixo ilustra um exemplo deste tipo de operações.

char *c, *v;

scanf("%c %c", c, v);if (c == v)     printf("As variáveis estao na mesma posicao.\n");else    printf("As variaveis nao estao na mesma posicao.\n");

Ponteiros e Vetores

Ponteiros e Vetores estão fortemente relacionados na linguagem C. O nome de um vetor é um ponteiro que aponta para a primeira posição do vetor e todas as operações já mencionadas para ponteiros podem ser executadas com um nome de vetor. Por exemplo, a declaraçãoint v[100];

declara um vetor de inteiros de 100 posições e a partir dela temos que v é um ponteiro equivalente ao da declaração abaixoint *v;

Por esta razão as seguintes declarações são idênticas e podem ser intercambiadas independemente do modo como v foi declarado:

v[i] == *(v+i)&v[i] == v+i

Page 29: Ponte Iros

O exemplo (c8vetpon.c) ilustrado abaixo mostra as duas notações sendo usadas para imprimir o mesmo vetor.

void main(void){    float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};    int i;    for (i=0; i<9; i++) printf("%.1f ", v[i]);    printf("\n");    for (i=0; i<9; i++) printf("%.1f ", *(v+i));}

Existe uma diferença fundamental entre declarar um conjunto de dados como um vetor ou através de um ponteiro. Na declaração de vetor a compilador automaticamente reserva um bloco de memória para que o vetor seja armazenado. Quando apenas um ponteiro é declarado a única coisa que o compilador faz é alocar um ponteiro para apontar para a memória, sem que espaço seja reservado.

 O nome de um vetor é chamado de ponteiro constante e, portanto, não pode ter o seu valor alterado. Assim, os comandos abaixo não são válidos:

void main(){    int list[5], i;

    /* O ponteiro list nao pode ser modificado recebendo o endereco de i */    list = &i    /* O ponteiro list nao pode ser incrementado */      list++; }

Para percorrer um vetor além da maneira mostrada no exemplo (c8vetpon.c) é possível usar um ponteiro variável como ilustrado no exemplo (c8povar.c) abaixo.

void main(void){            float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};    int i;    float *p;

    for (i=0; i<9; i++) printf("%.1f ", v[i]);    printf("\n");    for (i=0; i<9; i++) printf("%.1f ", *(v+i));    printf("\n");    for (i=0, p=v; i<9; i++, p++) printf("%.1f ", *p);}

Observe como o ponteiro p recebe seu valor inicial e a maneira que ele é incrementado.

Page 30: Ponte Iros

Ponteiros e Strings

Um string constante é escrito como no exemplo:

 "Este e um string".

Até agora um dos usos mais comuns de strings constantes tem sido na função printf, como no exemplo abaixo

printf("Acabou o programa.\n");

Quando um string como este é enviado para a função o que é passado é o ponteiro para o string. É possível então carregar o endereço do string em um ponteiro do tipo char.void main(void){    char *lista;

    lista = "Ola como vai?";    printf("%s", lista);}

No exemplo c8postr.c aparece uma função que conta o número de caracteres de um string. Observe o ponteiro para o string constante e na função o ponteiro *(s+tam++) apontando caracter a caracter.    int strtam(char *s);

void main(void){    char *lista="1234567890";

    printf("O tamanho do string \"%s\" e %d caracteres.\n", lista, strtam(lista));    printf("Acabou.");}

int strtam(char *s){    int tam=0;

    while(*(s + tam++) != '\0');    return tam-1;}

Um outro exemplo (c8posta.c) ilustrado abaixo mostra uma função que copia um string para outro.int strcop(char *d, char *o);

void main(void){    char destino[20];    char  *origem="string de origem";    strcop(destino, origem);  /* copia o string origem para o destino */

Page 31: Ponte Iros

    printf("%s\n", origem);    printf("%s\n", destino);    printf("Acabou.\n");}

int strcop(char *d, char *o){    while ((*d++ = *o++) != '\0');    return 1;}

Alocação Dinâmica de Memória

As funções básicas de alocação de memória são malloc(), calloc() e  free() . Estas funções são encontradas na biblioteca stdlib.h.

  O exemplo (c8call1.c) abaixo ilustra o uso das função calloc e free.

#include <stdio.h>#include <stdlib.h>

void main(void){    float *v;    int i, tam;

    printf("Qual o tamanho do vetor? ");    scanf("%d", &tam);    v = calloc(tam, sizeof(float));    if (v){        for (i=0; i<tam; i++){            printf("Elemento %d ?", i);            scanf("%f", v+i);            printf("Li valor %f \n", *(v+i));        }        free(v);    }    else        printf("Nao consegui alocar memoria.");}

Um outro exemplo, agora empregando a função malloc() está mostrado no exemplo maior.c. Observe que neste exemplo também mostramos exemplos onde um endereço de variável foi passado para uma função de modo que a função main possa receber um valor (vezes).

void LeVetor (float *v, int tam);float ProcuraMaior (float *v, int tam, int *vezes);

int main(void){ float *v, maior; int i, tam, vezes;

printf("Qual o tamanho do vetor? ");

Page 32: Ponte Iros

scanf("%d", &tam); v = (float *) malloc(tam * sizeof(float)); if (v){ LeVetor(v, tam); maior = ProcuraMaior (v, tam, &vezes); printf("O maior elemento e %f e aparece %d vezes.\n.", maior, vezes); free(v); } else { printf("Nao consegui alocar memoria."); exit(1); } exit(0);}

void LeVetor (float *v, int tam) { int i;

for (i=0; i<tam; i++){ printf("Elemento %d ?", i); scanf("%f", v+i); printf("Li valor %f \n", *(v+i)); }}

float ProcuraMaior (float *v, int tam, int *vezes) { int i; float maior;

maior = v[0]; *vezes = 1; for (i=1; i<tam; i++) { if (v[i] > maior) { maior = v[i]; *vezes = 1; } else if (maior == v[i]) *vezes=*vezes+1;; } return maior;}

Ponteiros e Matrizes

Um ponteiro aponta para uma área de memória que é endereçada de maneira linear. Deste modo, é necessário mapear o endereço de cada elemento na matriz, que é dado por linha coluna, em um endereço linear.

Considere uma matriz chamada matriz de tamanho LIN,COL que poderia ser declarada e ter um de seus elementos lidos da seguinte maneira:

#define LIN 3#define COL 4void main(){    int matriz[LIN][COL];

Page 33: Ponte Iros

   for(i=0; i<LIN; i++){        for (j=0; j<COL; j++){            printf("Elemento %d %d = ", i, j);            scanf("%d", matriz[i][j]);        }    } }

Caso o programa utilizasse ponteiros ao invés de notação de matrizes o trecho de programa ficaria da seguinte maneira:

#define LIN 3#define COL 4

void main(void){    int *matriz;    int i, j;        matriz = malloc(LIN*COL*sizeof(int));    if (!matriz){        printf("Nao consegui alocar a memoria suficiente.\n");        exit(1);    }    for(i=0; i<LIN; i++){        for (j=0; j<COL; j++){            printf("Elemento %d %d = ", i, j);            scanf("%d", matriz+(i*COL+j));        }}

Observe que o endereço de cada elemento da matriz teve de ser calculado explicitamente. O programa completo que mostra como a matriz poderia ser impressa está indicada no exemplo c8pomat.c.

Uma outra possibilidade é utilizar vetores de ponteiros, onde cada linha corresponde a um vetor que é apontado por um ponteiro. No item seguinte (Vetores de ponteiros) detalhamos este exemplo.

Vetores de Ponteiros

Como ponteiros também são variáveis é possível então criar vetores de ponteiros e utilizá-los. O programa exemplo c8vepon.c mostra um programa onde é utilizado um vetor de ponteiros para linhas de caracteres.  #define LINHAS 10#define COLUNAS 60

/* prototipos das funcoes */void alocaespaco(char *linha[]);void lelinhas (char *linha[]);void imprimelinhas (char *linha[]);

Page 34: Ponte Iros

void ordenalinhas (char *linha[]);

void main(void){    char *linha[LINHAS];    alocaespaco(linha);    lelinhas(linha);    imprimelinhas(linha);    ordenalinhas(linha);    imprimelinhas(linha);}/* Esta rotina aloca espaco para as linhas */void alocaespaco(char *linha[]){    int i;       for (i=0; i<LINHAS; i++){        if (!(linha[i] = malloc(COLUNAS*sizeof(int)))){            printf("Nao consegui alocar o vetor %d.\n", i);            exit(i);        }    }}

/* Esta rotina le linhas */void lelinhas (char *linha[]){    int i;

    for (i=0; i<LINHAS; i++){        printf("Entre com a linha %d.\n", i);        gets(linha[i]);    }}

/* Esta rotina imprime as linhas */void imprimelinhas (char *linha[]){    int i;

    for (i=0; i<LINHAS; i++){        printf("Linha %d %s.\n", i, linha[i]);    }}

void ordenalinhas( char *linha[]){     char trocou;     char *p;     int i;

     do {        trocou = 0;        for (i=0; i<LINHAS-1; i++){            if (strcmp(linha[i], linha[i+1]) >0){               p = linha[i];               linha[i] = linha[i+1];               linha[i+1] = p;               trocou = 1;            }        }     } while (trocou);

}

Page 35: Ponte Iros

Observe na rotina main a declaração

char *linha[LINHAS];

que define um vetor de tamanho LINHAS. Este vetor contem ponteiros e não valores. Até este momento temos apenas posições reservadas para armazenar ponteiros. A alocação de espaço e a inicialização dos ponteiros é feita na rotina alocaespaco.

Como cada elemento do vetor é um ponteiro o ponteiro retornado pela função malloc é armazenado em cada um dos elementos do vetor. Portanto linha[i]é um ponteiro é pode ser usado como parâmetro das funções gets e printf.

Um ponto importante da ordenação das linhas é que a troca de posição das linhas involve somente a troca dos ponteiros das linhas a serem trocadas e não o seu conteúdo, o que é muito mais rápido.

Ponteiros para Ponteiros

No exemplo anterior podemos observar que o número de linhas da matriz é fixa, e portanto, há uma mistura de notação de ponteiros com matrizes. Vamos considerar um exemplo onde tanto o número de linhas como o de colunas é desconhecido. Neste exemplo iremos criar um vetor de ponteiros que irá armazenar o endereço inicial de cada linha.  Portanto, para obter um elemento da matriz primeiro devemos descobrir onde está a linha no vetor que armazena os endereços das linhas, em seguida procuramos na linha o elemento. A figura 8.1 ilustra como será feito o armazenamento desta matriz.

Page 36: Ponte Iros

O programa exemplo (c8pp.c), listado a seguir, irá pedir ao usuário que digite o número de linhas e colunas da matriz. Em seguida lerá todos os elementos da matriz e por último irá trocar duas linhas da matriz de posição. Observe que agora foi criado um ponteiro para ponteiro chamado de **matriz. O programa primeiro pergunta o número de linhas da matriz para poder alocar espaço para armazenar os ponteiros para cada uma das linhas. Em seguida é alocado espaço para armazenar cada uma das linhas. Notar que a troca de linhas da matriz involve simplesmente a troca de dois ponteiros e não a troca de todos os elementos das linhas. Esta solução é muito mais rápida do que a anterior, especialmente para matrizes grandes.

#include<stdio.h> #include<stdlib.h>

void main () {

     int **matriz;   /* ponteiro para os ponteiros de cada uma das

linhas */      int lin, col;   /* número de linhas e colunas */      int i, j;      int linha1, linha2; /* linhas da matriz que serao trocadas */      char linha[80]; /* linha de caracteres com os dados */      int *temp;

Page 37: Ponte Iros

     do {         puts("Qual o numero de linhas?");         gets(linha);         lin = atoi(linha);      } while (lin<=0);      printf("Numero de linhas = %d\n", lin);      printf("Alocando espaço para armazenar os ponteiros para as

linhas.\n");      matriz = (int **) malloc (lin * sizeof(int *));      if (!matriz) {         puts("Nao há espaço para alocar memória");         exit(1);      }

     do {         puts("Qual o numero de colunas?");         gets(linha);         col = atoi(linha);      } while (col<=0);      printf("Numero de colunas = %d\n", lin);      printf("Alocando espaço para armazenar os vetores de linhas.\n");      for (i=0; i<lin; i++)      {         *(matriz +i) = (int *) malloc(col * sizeof (int));         if (! *(matriz+i) ){            printf("Não há espaço para alocar a linha %d", i);            exit(1);         }      }      puts("Entre com os dados");      for (i=0; i<lin; i++)      {         printf("Entre com a linha %d\n", i);         for (j=0; j<col; j++)         {             printf("Elemento %d %d\n", i, j);             scanf("%d", *(matriz +i) +j);         }      }      puts("Dados lidos");      for (i=0; i<lin; i++)      {         for (j=0; j<col; j++)         {             printf("%7d ", *(*(matriz +i) +j)); 

Page 38: Ponte Iros

        }         printf("\n");      }      getchar();

     do {         puts("Qual a primeira linha a ser trocada?");         gets(linha);         linha1=atoi(linha);      } while (linha1 >=lin);

     do {         puts("Qual a segunda linha a ser trocada?");         gets(linha);         linha2=atoi(linha);      } while (linha2 >=lin);        temp = *(matriz + linha1);      *(matriz + linha1) = *(matriz + linha2);      *(matriz + linha2) = temp;        puts("Dados trocados.");      for (i=0; i<lin; i++)      {         for (j=0; j<col; j++)         {             printf("%7d ", *(*(matriz +i) +j));         }         printf("\n");      }

}  

A seguir mostramos (c8ppf.c) o exemplo anterior modificado para utilizar funções. O propósito é mostrar como ficam as chamadas e as definições das funções que utilizam ponteiros para ponteiros.

#include<stdio.h> #include<stdlib.h>

int **aloca_linhas(int ); void aloca_colunas (int **, int, int); void le_dados(int **, int, int ); void imprime_matriz(int **, int, int ); void troca_linhas (int **, int, int);

Page 39: Ponte Iros

void main () {      int **matriz;   /* ponteiro para os ponteiros de cada uma das

linhas */      int lin, col;   /* número de linhas e colunas */      int linha1, linha2; /* linhas da matriz que serao trocadas */      char linha[80]; /* linha de caracteres com os dados */

     do {         puts("Qual o numero de linhas?");         gets(linha);         lin = atoi(linha);      } while (lin<=0);      printf("Numero de linhas = %d\n", lin);

     printf("Alocando espaço para armazenar os ponteiros para as

linhas.\n");      matriz = aloca_linhas(lin);

     do {         puts("Qual o numero de colunas?");         gets(linha);         col = atoi(linha);      } while (col<=0);      printf("Numero de colunas = %d\n", lin);        printf("Alocando espaço para armazenar os vetores de linhas.\n");      aloca_colunas(matriz, lin, col);

     le_dados(matriz, lin, col);

     puts("Imprimindo dados lidos");      imprime_matriz(matriz, lin, col);

     getchar();

     do {         puts("Qual a primeira linha a ser trocada?");         gets(linha);         linha1=atoi(linha);      } while (linha1 >=lin);

     do {         puts("Qual a segunda linha a ser trocada?");         gets(linha);         linha2=atoi(linha);      } while (linha2 >=lin);

     troca_linhas(matriz, linha1, linha2);

Page 40: Ponte Iros

     puts("Imprimindo dados trocados.");      imprime_matriz(matriz, lin, col);

}

int **aloca_linhas(int lin) {

  int **m;      m = (int **) malloc (lin * sizeof(int *));      if (!m) {         puts("Nao há espaço para alocar memória");         exit(1);      }      return m; }

void aloca_colunas(int **matriz, int lin, int col) {   int i;      for (i=0; i<lin; i++)      {         *(matriz +i) = (int *) malloc(col * sizeof (int));         if (! *(matriz+i) ){            printf("Não há espaço para alocar a linha %d", i);            exit(1);         }      } }

void le_dados (int **matriz, int lin, int col) {   int i, j;      puts("Entre com os dados");      for (i=0; i<lin; i++)      {         printf("Entre com a linha %d\n", i);         for (j=0; j<col; j++)         {             printf("Elemento %d %d\n", i, j);             scanf("%d", *(matriz +i) +j);         }      } }

void imprime_matriz (int **matriz, int lin, int col) {      int i, j;

Page 41: Ponte Iros

     for (i=0; i<lin; i++)      {         for (j=0; j<col; j++)         {             printf("%7d ", *(*(matriz +i) +j));         }         printf("\n");      } }

void troca_linhas ( int **matriz, int linha1, int linha2) {

  int *temp;

  temp = *(matriz + linha1);   *(matriz + linha1) = *(matriz + linha2);   *(matriz + linha2) = temp; }    

Exercícios

1. O que será impresso pelo progama nome.c assumindoque a entrada fornecida é o seu nome completo. Assuma que não há nenhum espaço em branco antes do início do nome.

#include<stdio.h>#include<stdlib.h>#include<string.h>

#define DIM 81#define TAM 10

void LeS(char *, int );int espiona(char *, int *, int );void zera(int *, int );void imprimet(int *);

int main (void){

char nome[DIM];int t[TAM];

LeS(nome, DIM);espiona(nome, t, TAM);

Page 42: Ponte Iros

imprimet(t);

return 0;}

void zera(int *t, int tam){

int i;

for (i=0; i<tam; i++) {

t[i] = 0;}

}

int espiona (char *s, int *t, int tam){ int j;

char *p;

p = s;j = 0;zera(t, tam);

while (1){

if (!(*p)){

j++;t[j] = -1;return j-1;

}

if (*p != ' '){

t[j]++;p++;

}else{

j++;while (*p == ' ') p = p + 1;

}}

}

void imprimet(int *t){

int i;

i = 0;while (t[i] > 0){

printf("%d = %d\n", i, t[i]);i++;

}}

void LeS (char *s, int tam){

fgets(s, DIM, stdin);

Page 43: Ponte Iros

s[strlen(s)-1] = '\0';}

2. Escreva um programa que gere um vetor de três dimensões (X, Y e Z) em que cada posição guarda a soma de suas coordenadas. As dimensões da matriz deverão ser determinadas em tempo de execução e o programa deverá informar os valores gerados. Solução: vetor.c 

#include<stdio.h>#include<stdlib.h>

main(){

int *matriz, x_max, y_max, z_max, x, y, z;

puts("ENTRE COM AS DIMENSOES"); printf("\nDimensao 1 (X): "); scanf("%d", &x_max); printf("\nDimensao 2 (Y): "); scanf("%d", &y_max); printf("\nDimensao 3 (Z):"); scanf("%d", &z_max);

matriz = (int *)malloc(x_max*y_max*z_max*sizeof(int));

if(!matriz) puts("Nao consegui alocar memoria."); else{

for(x = 0; x < x_max; x++) for(y = 0; y < y_max; y++)

for(z = 0; z < z_max; z++) //atribui os valores as posicoes *(matriz + x*(y_max*z_max) + y*(z_max) + z) = (x + y + z);

for(x = 0; x < x_max; x++) for(y = 0; y < y_max; y++)

for(z = 0; z < z_max; z++) //exibe os valores das posicoes printf("\n%d+%d+%d=%2d", x, y, z, *(matriz + x*(y_max*z_max)

+ y*(z_max) + z)); }

free(matriz);

getche();

}

Page 44: Ponte Iros

3. Escreva um programa que leia uma frase de até 80 caracteres do teclado e imprima a frequência com que aparece cada uma das letras do alfabeto na frase. Solução: c8contac.c 

/* Programa: c8contac.c Objetivo: Verifica a frequencia de cada uma das letras do alfabeto em uma

frase de até 80 caracteres. Autor: Adriano Cruz Data: 28/03/2000*/

#include <ctype.h>#include <stdio.h>#include <stdlib.h>

#define TAM_TEXTO 80#define TAM_ALFABETO 26

void ZeraFreq (int *);void ParaMaius(char *);void ContaFreq(char *, int *);void ImprimeFreq (int *);

int main (void ) {

char frase[TAM_TEXTO]; int freq[TAM_ALFABETO];

ZeraFreq (freq);

fgets(frase, TAM_TEXTO, stdin); ParaMaius(frase); puts(frase); ContaFreq(frase, freq); ImprimeFreq(freq); exit(0);}

void ZeraFreq (int *freq) { int i;

for (i=0; i<TAM_ALFABETO; i++) *(freq+i)=0;}

void ParaMaius(char *frase) { int i;

for (i=0; *(frase + i) != '\0'; i++) { *(frase+i) = toupper(*(frase+i)); }}

void ContaFreq (char *frase, int *freq) {

Page 45: Ponte Iros

int i;

for (i=0; *(frase +i) != '\0'; i++) *(freq + *(frase + i) - 'A') = *(freq + *(frase + i) - 'A') + 1;}

void ImprimeFreq (int *freq) { int i; char c='A';

for (i=0; i<TAM_ALFABETO; i++) printf("%c = %d\n", c+i, *(freq+i));}

4. Escreva um programa que leia uma frase de até 80 caracteres e a imprima em ordem reversa convertendo todos os caracteres minúsculos para maiúsculos. Solução: reverso.c 

#include<stdio.h>#include<stdlib.h>#include<ctype.h>#include<string.h>

#define TAM 81

void nextP(char *v){

char c;

c = *v;if (c == '\0')

return;else{

nextP(v+1);putchar(c);return;

}}

int main(void){

char phrase[81];

int i;

fgets(phrase, TAM, stdin);

for (i=0; i<strlen(phrase); i++){

phrase[i] = toupper(phrase[i]);}

nextP(phrase);

Page 46: Ponte Iros

printf("\n");

return 0;}

5. Escreva um programa que leia uma matriz e a imprima. O programa deve ler o numero de colunas e linhas do teclado. O programa deve ainda trocar duas linhas da matriz de posição. Os números das linhas a serem trocadas devem ser lidos do teclado. Solução: c8trolin.c 

#include <stdio.h>

#include <alloc.h>

#include <stdlib.h>

void main(void)

{

int *matriz;

int i, j, lin1, lin2;

int temp, tam;

int linhas, colunas;

tam = sizeof(int);

printf("Entre com o numero de linhas e colunas.\n");

printf("Linhas? "); scanf("%d", &linhas);

printf("Colunas? "); scanf("%d", &colunas);

matriz = malloc(linhas*colunas*tam);

if (!matriz){

printf("Nao consegui alocar a memoria suficiente.\n");

exit(1);

Page 47: Ponte Iros

}

for(i=0; i<linhas; i++){

for (j=0; j<colunas; j++){

printf("Elemento %d %d = ", i, j);

scanf("%d", matriz+(i*colunas+j)*tam);

}

}

for(i=0; i<linhas; i++){

for (j=0; j<colunas; j++){

printf("El%2d,%2d = %2d ", i, j, *(matriz+(i*colunas+j)*tam));

}

printf("\n");

}

printf("Que linhas quer trocar? "); scanf ("%d %d", &lin1, &lin2);

for (i=0; i<colunas; i++){

temp = *(matriz + (lin1*colunas+i)*tam);

*(matriz + (lin1*colunas+i)*tam) = *(matriz + (lin2*colunas+i)*tam);

*(matriz + (lin2*colunas+i)*tam) = temp;

}

for(i=0; i<linhas; i++){

for (j=0; j<colunas; j++){

printf("El%2d,%2d = %2d ", i, j, *(matriz+(i*colunas+j)*tam));

}

printf("\n");

Page 48: Ponte Iros

}

free(matriz);

printf("Acabou.");

}

6. Escreva um programa que simule uma pilha usando vetores. O programa deve implementar as seguintes operações na pilha:

1. Inserir.

2. Remover

3. Listar

Solução: c8pilha.c 

/* programa: c8pilha.c objetivo: simular uma pilha em um vetor autor: adriano joaquim de oliveira cruz data: 18/11/1997*/

#include<stdlib.h>#include<stdio.h>#include<conio.h>#include<malloc.h>#include<ctype.h>

#define DIM 50

void lista ( int *pilha ){ int i;

i = *pilha; if (i) for ( ; i > 0 ; i--) printf("Elemento %d = %d\n", i, *(pilha + i)); else printf("Sinto muito, nao ha nada para listar.\n"); printf("Aperte qualquer tecla para continuar.\n"); getch();}

int insere ( int *pilha ){ int i;

i = *pilha; if (i == DIM-1) return -1;

Page 49: Ponte Iros

else { printf("Por favor entre com um valor.\n"); scanf("%d", (pilha + i + 1)); *pilha = *pilha + 1; return 0; }}

int retira ( int *pilha ){ int i=0, cheia;

cheia = *pilha; if (!cheia){ printf("Sinto muito, nao ha nada para retirar.\n"); i = -1; } else { printf("Retirando da pilha...\n"); *pilha = *pilha - 1; } printf("Aperte qualquer tecla para continuar.\n"); getch(); return i;}

char opcao(void ){// char c;

flushall(); clrscr(); printf("Qual a opcao?\n"); printf("\t[I]nserir na pilha.\n"); printf("\t[R]etirar da pilha.\n"); printf("\t[L]istar pilha.\n"); printf("\t[T]erminar.\n"); return toupper(getch());}

void main(){ int *pilha; char continua=1, c;

clrscr(); pilha = (int *) malloc ( DIM * sizeof(int)); if (!pilha) { printf("Epa, sinto muito nao ha espaco."); exit (0); }

*pilha = 0; /* armazena quantos elementos estao armazenados na pilha */

do { c = opcao(); switch (c) { case 'I': if (insere (pilha)<0) printf("Sinto muito, pilha cheia.\n"); break; case 'R': if (retira (pilha) < 0)

Page 50: Ponte Iros

printf("Epa, a pilha esta vazia."); break; case 'L': lista (pilha); break; case 'T': continua = !continua; break; } } while ( continua ); printf("Estamos encerrando as atividades."); free(pilha); getch();}

7. Escreva uma função que receba um ponteiro para uma string e troque todo o caracter após um branco pelo seu equivalente maiúsculo. Solução: c8maiu.c 

/*

programa: c8maiu.c

objetivo: Trocar letras para maiusculas

autor: adriano joaquim de oliveira cruz

data: 20/03/2000

*/

#include<stdlib.h>

#include<stdio.h>

#define TAM_TEXTO 20

int PaMaius(char *);

int main ( void ) {

char texto[TAM_TEXTO];

Page 51: Ponte Iros

printf("Qual o texto?\n"); fgets(texto, TAM_TEXTO, stdin);

printf("Antes: %s\n", texto);

PaMaius(texto);

printf("Depois: %s\n", texto);

exit(0);

}

int PaMaius (char *t) {

int i=0;

while (*(t+i) != '\0') {

if (*(t+i) >= 'a' && *(t+i) <= 'z') *(t+i) = 'A' + *(t+i) - 'a';

i++;

}

return 0;

}

8. Escreva um programa que leia seu nome completo e pergunte quantas letras tem o seu primeiro nome. Assuma que a letra 'a' tem índice 0, a letra 'b' índice 1 e assim por diante. O programa deve imprimir quantas letras iguais a letra cujo índice é o número de letras do seu primeiro nome existem no seu nome completo. Solução: diga.c 

/* programa: diga.c objtivo: Contar a frequencia das letras em um texto lido de arquivo. autor: adriano joaquim de oliveira cruz data: 25/05/2001*/

#include<stdlib.h>#include<stdio.h>

Page 52: Ponte Iros

#define TAM_NOME 80

void Qual (int , char *);int Quantas (char *, char );

int main ( void ) { char linha[80]; char nome[80]; int letras, total; char c='a'; puts("Entre com seu nome completo em letras minusculas."); gets(nome); puts("Entre com o numero de letras de seu primeiro nome."); gets(linha); letras = atoi(linha);

Qual(letras, &c); total = Quantas (nome, c); printf("total = %3d\n", total); exit(0);}

void Qual (int n, char *c) { *c = *c + n;}

int Quantas(char *v, char c) { int q=0, i=0; while (v[i] != '\0') { if (*(v+i) == c) q++; i++; } return q;}

9. Faça uma função que verifique se a expressão apontada por substr está presente na cadeia apontada por str. A função possui o protótipo abaixo:int posicao(char *substr, char *str);

e retorna a posição em que a sub-cadeia aparece em cadeia. Solução: procura.c 

/* Descricao: Algoritmo para encontrar substrings; Autores: Alexandre Stauffer & Jose Maciel; Data: 03/2001*/

#define CARACS 256#include <stdio.h>

Page 53: Ponte Iros

#include <string.h>

struct _lista{

int desvio;struct _lista *prox;

}mapa[CARACS];

void criar_mapa(char *str, int n){

int i;char c;struct _lista *p;

for(i=0; i<CARACS; i++){

mapa[i].desvio=n;mapa[i].prox=NULL;

}

for(i=n-1; i>=0; i--){

c=str[i];if(mapa[c].desvio==n)

mapa[c].desvio=n-i-1;else{

p=&mapa[c];while(p->prox)

p=p->prox;p->prox=(struct _lista*)malloc(sizeof(struct

_lista));p=p->prox;p->desvio=n-i-1;p->prox=NULL;

}}

}

int mapear(char str){

static int i;static char k;int j;struct _lista *p;

if(str){

i=0;k=str;

}

p=&mapa[k];j=i;

while(j>0){

p=p->prox;j--;

}

Page 54: Ponte Iros

i++;return p->desvio;

}

int posicao(char *substr, char *str){

int i,l1,l2,j,pos=-1,x,m;

l1=strlen(str);l2=strlen(substr);if(!(l1*l2))

return -1;criar_mapa(substr,l2);j=0;i=l2-1;while(i<l1){

x=l2-j-1;if(str[i-j]==substr[x]){

if(!x){

pos=i-j;i=l1;

}else

j++;}else{

m=mapear(str[i-j]);while(m<j)

m=mapear(0);i+=m-j;j=0;

}}return pos;

}

int main(){

char cadeia1[80], cadeia2[10];

// 0 5 0 5 0 5 0 5 0strcpy(cadeia1, "A mao que afaga eh a mesma que apedreja");strcpy(cadeia2, "mesma");printf("%d\n",posicao(cadeia2,cadeia1));

}

Page 55: Ponte Iros

10.Escreva um programa que procure em uma matriz elementos que sejam ao mesmo tempo o maior da linha e o menor coluna. As dimensões da matriz devem ser pedidas ao usuário. Solução: sela.c 

/* programa: sela.c descricao: Verificar se uma matriz contem um ponto de sela, definido como elemento que é o maior de uma linha e o menor de sua

coluna. autor: adriano joaquim de oliveira cruz data: 07/07/2003*/

#include<stdlib.h>#include<stdio.h>

float **AlocaMatriz (int , int);void PontosSela( float **, int, int);void LeMatriz (float **, int, int);

int main (void){

int l, /* numero de linhas da matriz */c; /* numero de colunas da matriz */

float **m;

puts("Numero de linhas da matriz"); scanf("%d", &l);puts("Numero de colunas da matriz"); scanf("%d", &c);

if (!(m = AlocaMatriz(l, c))){

puts("Nao ha espaco para a matriz.");exit(EXIT_FAILURE);

}

LeMatriz (m, l, c);

PontosSela(m, l, c);

exit (EXIT_SUCCESS);}

/* * Aloca espaco para uma matriz de tamanho l por c */

float **AlocaMatriz (int l, int c){

float **m;int i;

m = (float **) malloc ( l * sizeof (float *));if (!m){

return m;

Page 56: Ponte Iros

}

for (i =0 ; i<l; i++){

*(m+i) = (float *) malloc ( c * sizeof(float));

if (! *(m+i) ){

return NULL;}

}return m;

}

void LeMatriz (float **m, int l, int c){

int i, j;

for (i=0; i<l; i++)for (j=0; j<c; j++){

printf("Elemento %d %d?", i, j);

scanf("%f", *(m+i) + j);}

}

void PontosSela( float **m, int l, int c){

int i, j;int maior, menor;

for (i=0; i<l; i++) /* procuro maior em cada linha */{

maior = 0;for (j=1; j<c; j++){

maior = m[i][j] > m[i][maior] ? j : maior;

}/* o maior da linha i esta na coluna

maior m[i][maior]*//* agora anda pela coluna maior

procurando o menor */menor = 0;for (j=1; j<l; j++){

menor = m[j][maior] < m[menor][maior] ? j : menor;

}/* o menor da coluna maior esta linha

menor m[menor][maior] */if (i == menor){

printf("Ha um ponto de sela em %d %d\n", i, maior);

}}

}

Page 57: Ponte Iros

11.Escreva um programa que leia duas cadeias de caracteres e concatene a segunda cadeia ao final da primeira. Solução: concatena.c 

/* * Programa: concatena.c * Autor: Adriano Cruz * Descricao: Le dois vetores de caracteres e os concatena * Assume que o primeiro vetor cabe no segundo. * Data: 22/05/2005 */

#include<stdio.h>

#define MAX 81

int tiraCR (char *v);void concat (char *p1, char *p2);

int main (void){

char vetor1[MAX]; /* primeiro vetor de caracteres */char vetor2[MAX/2]; /* segundo vetor de caracteres */

/* Le primeiro Vetor */fgets(vetor1, MAX, stdin);tiraCR(vetor1);

/* Le segundo vetor */fgets(vetor2, MAX/2, stdin);tiraCR(vetor2);

/* concatena o segundo vetor ao final do primeiro */concat(vetor1, vetor2);

/* imprime o resultado da concatenacao */puts(vetor1);

/* imprime o segundo vetor */puts(vetor2);

return 0;

}

/* * tiraCr: Tira o caracter CR do final do vetor, se houver. * * parametros de entrada: * ponteiro para o vetor de onde o CR deve ser retirado * parametros de saida: * 0 - se tirou o CR * 1 - se nao havia CR * * autor: Adriano Cruz * * data: 22/05/2005

Page 58: Ponte Iros

*/int tiraCR (char *v){

int tam;

for (tam=0; (*(v+tam) != '\0') && (*(v+tam) != '\n'); tam++);

if (*(v+tam) == '\n'){

*(v+tam) = '\0';return 0;

}else{

return 1;}

}

/* * concat: concatena um vetor ao final de outro. * * parametros de entrada: * p1: ponteiro para o vetor que vai receber o segundo vetor * p2: ponteiro para o vetor que sera copiado * parametros de saida: * nao ha * * autor: Adriano Cruz * * data: 22/05/2005 */void concat (char *p1, char *p2){

int i, j;

i = j = 0;

while (*(p1 + i++) != '\0');i--;

while ((*(p1 + i++) = *(p2 + j++)));

*(p1 + i) = '\0';}

Page 59: Ponte Iros

AED – Semestre 2005.2Lista de Revisão de Ponteiros

 1.      Seja o seguinte trecho de programa:

int i=3,j=5;int *p, *q;p = &i;q = &j;Qual é o valor das seguintes expressões ?a) p == &i;     b) *p - *q              c) **&p          d) 3* - *p/(*q)+7

  2.      Qual será a saída deste programa supondo que i ocupa o

endereço 4094 na memória?main() {  int i=5, *p;  p = &i;  printf(“%x %d  %d  %d  %d \n”, p,*p+2,**&p,3**p,**&p+4);}

  3.      Se i e j são variáveis inteiras e p e q ponteiros para int, quais das

seguintes expressões de atribuição são ilegais?a) p = &i;                    b) *q = &j;                       c) p =

&*&i;         d) i = (*&)j;e) i = *&j;                 f) i = *&*&j;       g) q = *p;              h) i

= (*p)++ + *q  4.      Qual serão as saídas do seguinte programa?

 #include <stdio.h>#include <conio.h> int main() {  int    valor;  int   *p1;  float  temp;  float *p2;  char   aux;  char  *nome = "Algoritmos";  char  *p3;  int    idade;  int   vetor[3];  int   *p4;  int   *p5;   /* (a) */

Page 60: Ponte Iros

  valor = 10;  p1 = &valor;  *p1 = 20;  printf("(a) %d \n", valor);   /* (b) */  temp = 26.5;  p2 = &temp;  *p2 = 29.0;  printf("(b) %.1f \n", temp);   /* (c) */  p3 = &nome[0];  aux = *p3;  printf("(c) %c \n", aux);   /* (d) */  p3 = &nome[4];  aux = *p3;  printf("(d) %c \n", aux);   /* (e) */  p3 = nome;  printf("(e) %c \n", *p3);   /* (f) */  p3 = p3 + 4;  printf("(f) %c \n", *p3);   /* (g) */  p3--;  printf("(g) %c \n", *p3);   /* <h> */  vetor[0] = 31;  vetor[1] = 45;  vetor[2] = 27;  p4 = vetor;  idade = *p4;  printf("(h) %d \n", idade);   /* (i) */  p5 = p4 + 1;  idade = *p5;  printf("(i) %d \n", idade);   /* (j) */  p4 = p5 + 1;  idade = *p4;  printf("(j) %d \n", idade);   /* (l) */  p4 = p4 - 2;  idade = *p4;  printf("(l) %d \n", idade);   /* (m) */  p5 = &vetor[2] - 1;  printf("(m) %d \n", *p5);   /* (n) */

Page 61: Ponte Iros

  p5++;  printf("(n) %d \n", *p5);    return(0);}

  5.      Qual é o resultado do seguinte programa? #include <conio.h>#include <stdio.h>void main(){      float vet[5] = {1.1,2.2,3.3,4.4,5.5};      float *f;      int i;      f = vet;      printf("contador/valor/valor/endereco/endereco");      for(i = 0 ; i <= 4 ; i++){            printf("\ni = %d",i);            printf("   vet[%d] = %.1f",i, vet[i]);            printf("   *(f + %d) = %.1f",i, *(f+i));            printf("   &vet[%d] = %X",i, &vet[i]);            printf("   (f + %d) = %X",i, f+i);      }}

  6.      Assumindo que pulo[] é um vetor do tipo int, quais das seguintes

expressões referenciam o valor do terceiro elemento da matriz?a) *(pulo + 2)          b) *(pulo + 4)             c) pulo + 4                       d) pulo + 2

  7.      Supor a declaração: int mat[4], *p, x; Quais expressões são

válidas? Justifique:a) p = mat + 1;       b) p = mat++;      c) p = ++mat;      d) x = (*mat)++;

  8.      O que fazem os seguintes programas?  #include <conio.h>#include <stdio.h>void main(){  int vet[] = {4,9,13};  int i;  for(i=0;i<3;i++){    printf("%d ",*(vet+i));  }}

 #include <conio.h>#include <stdio.h>void main(){  int vet[] = {4,9,13};  int i;  for(i=0;i<3;i++){    printf("%X ",vet+i);  }

 #include <conio.h>#include <stdio.h>void main(){  int vet[] = {4,9,13};  int i;  for(i=0;i<3;i++){    printf("%X ",vet+i);  }

Page 62: Ponte Iros

  }

 }

   9.      O que faz o seguinte programa quando executado? #include <conio.h>#include <stdio.h>void main() {      int vet[] = {4,9,12};      int i,*ptr;      ptr = vet;      for(i = 0 ; i < 3 ; i++) {           printf("%d ",*ptr++);      }}

#include <conio.h>#include <stdio.h>void main(){      int vet[] = {4,9,12};      int i,*ptr;      ptr = vet;      for(i = 0 ; i < 3 ; i++) {           printf("%d ",(*ptr)++);      }}

(a) (b)

   10.  Seja vet um vetor de 4 elementos: TIPO vet[4]. Supor que depois da

declaração, vet esteja armazenado no endereço de memória 4092 (ou seja, o endereço de vet[0]). Supor também que na máquina usada uma variável do tipo char ocupa 1 byte, do tipo int ocupa 2 bytes, do tipo float ocupa 4 bytes e do tipo double ocupa 8 bytes.

 Qual o valor de vet+1, vet+2 e vet+3 se:a)      vet for declarado como char?b)      vet for declarado como int?c)      vet for declarado como float?d)      vet for declarado como double?

Page 63: Ponte Iros

Projeto de Algoritmos

Home      |      Prefácio      |      Livros      |      Sítios WWW      |      Índice

Endereços e ponteiros

Os conceitos de endereço e ponteiro são fundamentais em qualquer linguagem de programação (embora sejam mais visíveis em C que em outras linguagens).  O conceito de ponteiro é muito útil mas difícil; seu domínio exige um certo esforço.

Veja o verbete Pointer na Wikipedia.

Endereços

A memória de qualquer computador é uma sequência de bytes.  Cada byte armazena um de 256 possíveis valores.  Os bytes são numerados sequencialmente.  O número de um byte é o seu endereço (= address).

Cada objeto na memória do computador ocupa um certo número de bytes consecutivos. No meu computador, um char ocupa 1 byte, umint ocupa 4 bytes e um double ocupa 8 bytes

Cada objeto na memória do computador tem um endereço. Na maioria dos computadores, o endereço de um objeto é o endereço do seu primeiro byte. Por exemplo, depois das declarações

char c;

int i;

struct {

int x, y;

} ponto;

int v[4];

os endereços das variáveis poderiam ser

c 89421

Page 64: Ponte Iros

i 89422

ponto 89426

v[0] 89434

v[1] 89438

v[2] 89442

(o exemplo é fictício).  O endereço de uma variável é dado pelo operador  &.  (Não confunda esse uso de "&" com o operador lógicoand, que em C se escreve "&&".)  Se  i  é uma variável então  &i  é o seu endereço.  No exemplo acima,   &i   vale  89422   e   &v[3]  vale  89446.

Exemplo:  O segundo argumento da função de biblioteca scanf é o endereço da posição na memória onde devem ser depositados os objetos lidos no dispositivo padrão de entrada:

int i;

scanf( "%d", &i);

Ponteiros

Um ponteiro (= apontador = pointer) é um tipo especial de variável que armazena endereços.  Um ponteiro pode ter o valor especial

NULL

que não é o endereço de lugar algum.  A constante NULL está definida no arquivo-interface stdlib e seu valor é 0 na maioria dos computadores. 

Se um ponteiro p armazena o endereço de uma variável i, podemos dizer "p aponta para i" ou "p é o endereço de i"  (querendo dizer "o ponteiro p aponta para i" no primeiro caso e "o valor de p é o endereço de i" no segundo).  Se um ponteiro p tem valor diferente de NULLentão

*p

Page 65: Ponte Iros

é o valor do objeto apontado por p.  (Não confunda esse uso de "*" com o operador de multiplicação!)  Por exemplo, se i é uma variável ep é igual a &i então dizer "*p" é o mesmo que dizer "i".

              

Figura esquerda:  um ponteiro p, armazenado no endereço 90001, contém o endereço de um inteiro.  Figura 

direita:  representação esquemática da situação.

Há vários tipos de ponteiros: ponteiros para caracteres, ponteiros para inteiros, ponteiros para ponteiros para inteiros, ponteiros pararegistros etc.  O computador faz questão de saber de que tipo de ponteiro você está falando. Para declarar um ponteiro p para um inteiro, diga

int *p;

Para declarar um ponteiro p para um registro cel, diga

struct cel *p;

Um ponteiro r para um ponteiro que apontará um inteiro é declarado assim:

int **r;

Exemplos

Suponha que a, b e c são variáveis inteiras. Eis um jeito bobo de fazer  "c = a+b":

int *p; /* p é um ponteiro para um inteiro */

int *q;

p = &a; /* o valor de p é o endereço de a */

q = &b; /* q aponta para b */

c = *p + *q;

Outro exemplo bobo:

Page 66: Ponte Iros

int *p;

int **r; /* r é um ponteiro para um ponteiro para um inteiro */

p = &a; /* p aponta para a */

r = &p; /* r aponta para p e *r aponta para a */

c = **r + b;

Aplicação

Suponha que precisamos de uma função que troque os valores de duas variáveis inteiras, digamos i e j.  É claro que a função

void troca( int i, int j) /* errado! */

{

int temp;

temp = i; i = j; j = temp;

}

não produz o efeito desejado, pois recebe apenas os   valores   das variáveis  e não as variáveis propriamente ditas.  A função recebe "cópias" das variáveis e troca os valores dessas cópias, enquanto as variáveis "originais" permanecem inalteradas.  Para obter o efeito desejado, é preciso passar à função os endereços das variáveis:

void troca( int *p, int *q)

{

int temp;

temp = *p; *p = *q; *q = temp;

}

Para aplicar a função às variáveis i e j basta dizer

troca( &i, &j);

ou ainda

int *p, *q;

Page 67: Ponte Iros

p = &i;

q = &j;

troca( p, q);

Exercícios1. Por que o código abaixo está errado?2. void troca( int *i, int *j) {3. int *temp;4. *temp = *i; *i = *j; *j = *temp;5. }

6. Um ponteiro pode ser usado para dizer a uma função onde ela deve depositar o resultado de seus cálculos. Escreva uma função hmque converta minutos em horas-e-minutos. A função recebe um inteiro mnts e os endereços de duas variáveis inteiras, digamos he m, e atribui valores a essas variáveis de modo que m seja menor que 60 e que 60*h + m seja igual a mnts.   Escreva também uma função main que use a função hm.

7. Escreva uma função mm que receba um vetor inteiro v[0..n-1] e os endereços de duas variáveis inteiras, digamos min e max, e deposite nessas variáveis o valor de um elemento mínimo e o valor de um elemento máximo do vetor.   Escreva também uma funçãomain que use a função mm.

Vetores e endereços

Os elementos de qualquer vetor (= array) têm endereços consecutivos na memória do computador.  [Na verdade, os endereços não são consecutivos, pois cada elemento do vetor pode ocupar vários bytes. Mas o compilador C acerta os detalhes internos de modo a criar a ilusão de que a diferença entre os endereços de elementos consecutivos vale 1.]  Por exemplo, depois da declaração

int *v;

v = malloc( 100 * sizeof (int));

o ponteiro v aponta o primeiro elemento de um vetor de 100 elementos. O endereço do segundo elemento do vetor é v+1 e o endereço do terceiro elemento é v+2.  Se i é uma variável do tipo int então

v + i

Page 68: Ponte Iros

é o endereço do (i+1)-ésimo elemento do vetor.  A propósito, as expressões  v + i  e  &v[i]  têm exatamente o mesmo valor e portanto as atribuições

*(v+i) = 87;

v[i] = 87;

têm o mesmo efeito.  Analogamente, qualquer dos fragmentos de código abaixo pode ser usado para "carregar" o vetor v:

for (i = 0; i < 100; ++i) scanf( "%d", &v[i]);

for (i = 0; i < 100; ++i) scanf( "%d", v + i);

 

Todas essas considerações também valem se o vetor for alocado pela declaração

int v[100];

mas nesse caso v é uma espécie de "ponteiro constante", cujo valor não pode ser alterado.

Exercícios4. Suponha que os elementos do vetor v são do tipo int e 

cada int ocupa 8 bytes no seu computador. Se o endereço de v[0] é55000, qual o valor da expressão  v + 3?

5. Suponha que v é um vetor declarado assim:6. int v[100];

Descreva, em português, a sequência de operações que deve ser executada para calcular o valor da expressão

&v[k + 9];

7. Suponha que v é um vetor. Descreva a diferença conceitual entre as expressões  v[3]  e  v + 3.

8. O que há de errado com o seguinte trecho de código?

9. char *a, *b;

10. a = "abacate";

11. b = "uva";

12. if (a < b)

Page 69: Ponte Iros

13. printf( "%s vem antes de %s no dicionário", a, b);

14. else

15. printf( "%s vem depois de %s no dicionário", a, b);

16.Diga (sem usar o computador) qual o conteúdo do vetor a depois dos seguintes comandos.

17. int a[99];

18. for (i = 0; i < 99; ++i) a[i] = 98 - i;

19. for (i = 0; i < 99; ++i) a[i] = a[a[i]];

 

Veja aula em video sobre ponteiros no AcademicEarth Veja aula em video sobre aritmética de ponteiros no AcademicEarth

URL of this site: www.ime.usp.br/~pf/algoritmos/Last modified: Thu Sep 5 08:07:56 BRT 2013 Paulo FeofiloffIME-USP

    

 

 

 

 

 

 

 

 

Page 70: Ponte Iros