linguagem c ponteiros -...
TRANSCRIPT
Linguagem C – ponteiros
IF61A/IF71A - Computação 1
Prof. Leonelo Almeida
Universidade Tecnológica Federal do Paraná
Até agora ...
• Introdução à linguagem C
▫ ...
▫ Vetores
▫ Matrizes
▫ Funções
▫ Recursão
▫ Registros
Aula de hoje
• E se eu quiser uma função que retorne dois valores. Um inteiro e um ponto flutuante?
▫ Funções retornam somente um valor simples
Aula de hoje
• E se eu quiser uma função que retorne dois valores. Um inteiro e um ponto flutuante?
▫ Funções retornam somente um valor simples
• E se eu quiser uma função que altere as variáveis simples que forem passadas como parâmetro?
Aula de hoje
• E se eu quiser uma função que retorne dois valores. Um inteiro e um ponto flutuante?
▫ Funções retornam somente um valor simples
• E se eu quiser uma função que altere as variáveis simples que forem passadas como parâmetro?
▫ Funções não alteram as variáveis externas simples passadas como parâmetro
Ponteiros
• São tipos especiais de dados usados para armazenar endereços de memória
• Armazena o endereço de memória de outra variável do tipo informado na declaração
• Declaração: tipo *nome_variavel;
• Exemplo: int *memA;
float *memB;
Armazena o endereço de memória de uma variável do tipo int
Armazena o endereço de memória de uma variável do tipo float
Operadores relacionados a ponteiros
• O operador &
▫ Retorna o endereço de memória de uma variável. Exemplo: int *memA;
int a=90;
memA = &a;
• O operador *
▫ Retorna o conteúdo do endereço apontado. Exemplo: printf(“%d”, *memA);
Organização
Endereço Valor
320954354 Abc
320954355 30
320954356 1
320954357 12e4545
320954358 90
320954359
a memA Memória
• Quando você usa a variável “a”, o programa retorna o valor contido na memória
• Quando você usa a variável “memA”, o programa retorna o endereço de memória
Exemplo #include <stdio.h>
int main(void) {
int *memA;
int a=90;
memA = &a;
printf(" %d --- %d", *memA, memA);
*memA = a;
printf("\n %d --- %d", *memA, memA);
memA = a;
printf("\n %d --- %d", *memA, memA);
return 0;
}
O que será impresso na tela?
Exemplo #include <stdio.h>
int main(void) {
int *memA;
int a=90;
memA = &a;
printf(" %d --- %d", *memA, memA);
*memA = a;
printf("\n %d --- %d", *memA, memA);
memA = a;
printf("\n %d --- %d", *memA, memA);
return 0;
}
90 --- um endereço
90 --- um endereço
Erro em tempo de execução: Segmentation fault
Exemplo 2
#include <stdio.h>
int main(void){
int b;
int *c;
b=10;
c=&b;
*c=11;
printf("%d",b);
}
O que será impresso na tela?
Exemplo 2
#include <stdio.h>
int main(void){
int b;
int *c;
b=10;
c=&b;
*c=11;
printf("%d",b);
}
11 Pois ambas as
variáveis apontam para o mesmo endereço
de memória
Exemplo 3
#include <stdio.h>
int main(void){
int num, q=1;
int *p;
num=100;
p = #
q = *p;
printf(“%d",q);
}
O que será impresso na tela?
Exemplo 4
...
int a, b;
int *c;
b = 10;
*c = 13;
...
Correto seria se, antes da atribuição da constante, um endereço fosse atribuído a “c”
...
int a, b;
int *c;
b = 10;
c = &a;
*c = 13;
...
Atenção
...
int a, b, c;
int *d;
b = 10;
c = 5;
d = &c;
a = b * d;
...
O operador * é usado em ponteiros e multiplicações
Atenção
...
int a, b, c;
int *d;
b = 10;
c = 5;
d = &c;
a = b * d;
...
O bloco à esquerda dará um erro de compilação. O correto seria ...
...
int a, b, c;
int *d;
b = 10;
c = 5;
d = &c;
a = b * (*d);
...
Exemplo 5
#include <stdio.h>
int main(void){
double b,a;
int *c;
b=10.89;
c=&b;
a=*c;
printf("%lf\n",a);
}
O que será impresso na tela?
Exemplo 5
#include <stdio.h>
int main(void){
double b,a;
int *c;
b=10.89;
c=&b;
a=*c;
printf("%lf\n",a);
}
Atribuição de tipos de dados incorreta.
Mensagem do compilador:
warning: assignment from incompatible pointer type
Vai imprimir um número diferente de 10.89
Resumindo ...
• Três usos para o operador *
• Na declaração de ponteiros:
▫ Exemplo: int *pont;
• No acesso ao valor apontado pelo ponteiro:
▫ Exemplo: printf(“%d”, *pont);
• Multiplicação de números:
▫ Exemplo: 10 * (*pont);
Indireção múltipla • Podemos ter um ponteiro que aponta para outro
ponteiro que aponta para um endereço. Isso também podem ser expandido para diversos níveis de indireção
• Apesar de possível é um conceito raro de ser usado e propenso a erros
• Exemplo: float **peso;
endereço endereço valor
ponteiro valor ponteiro
Operações com ponteiros int main(void){
double *a,*b, c, d;
b=&c;
a=&d;
if(b < a)
printf("\nO endereço apontado por b é menor: %p e %p", b, a);
else if(a < b)
printf("\nO endereço apontado por a é menor:%p e %p", a, b);
else if(a == b)
printf("Mesmo endereço");
if(*a == *b)
printf("Mesmo conteúdo: %lf", *a);
}
Operações com ponteiros int main(void){
double *a,*b, c, d;
b=&c;
a=&d;
if(b < a)
printf("\nO endereço apontado por b é menor: %p e %p", b, a);
else if(a < b)
printf("\nO endereço apontado por a é menor:%p e %p", a, b);
else if(a == b)
printf("Mesmo endereço");
if(*a == *b)
printf("Mesmo conteúdo: %lf", *a);
}
Para imprimir um ponteiro usamos a
máscara %p.
Operações com ponteiros
• Enquanto o ponteiro não estiver associado a um endereço é uma boa prática atribuir o valor NULL a ele
• Isto facilita as comparações para se saber se o ponteiro já tem um endereço válido associado
• Exemplo: ...
double *a = NULL, *b = NULL;
a = &c;
if (a != NULL) {
...
Passagem de parâmetros
• Passagem por valor
▫ Variáveis e constantes passadas por parâmetro para funções têm seus valores copiados para os parâmetros das funções (que são locais)
▫ Alterações nos parâmetros dentro da função não alteram as variáveis que foram passadas às funções
Exemplo de passagem por valor
...
void troca(int x, int y) {
int aux;
aux = x;
x = y;
y = aux;
}
int main(){
int x=4, y=5;
troca(x,y);
printf (“x = %d e y = %d”, x, y);
}
Exemplo de passagem por valor
...
void troca(int x, int y) {
int aux;
aux = x;
x = y;
y = aux;
}
int main(){
int x=4, y=5;
troca(x,y);
printf (“x = %d e y = %d”, x, y);
}
Será impresso: “x = 4 e y = 5”
Isso porque a troca ocorreu
com variáveis locais à função troca e, portanto, não afetam as variáveis da função main.
Passagem de parâmetros
• Passagem de argumentos por referência
▫ Em C só existe passagem de parâmetros por valor
▫ Em outras linguagens pode haver a passagem de parâmetros por referência
▫ Nesse tipo de passagem valores por referência podem ser alterados pela função que foi chamada
Passagem de parâmetros
• Algo parecido pode ser feito em C utilizando ponteiros
▫ Para isso basta passar como argumento de uma função o endereço da variável e não o seu valor
▫ Assim, alterações no valor contido no endereço que forem feitas pela função chamada afetarão a variável mesmo fora da função
Passagem de argumentos por referência #include <stdio.h>
void troca(int *end_x, int *end_y) {
int aux;
if(end_x != NULL && end_y != NULL){
aux = *end_x;
*end_x = *end_y;
*end_y = aux;
}
}
int main(){
int x=4, y=5;
troca(&x, &y);
printf("x = %d e y = %d\n", x, y);
}
Imprimirá: “x = 5 e y = 4”
Exemplo #include <stdio.h>
void maxAndMin(int vet[], int tam, int *min, int *max);
int main(){
int v[] = {10, 80, 5, -10, 45, -20, 100, 200, 10};
int min, max;
maxAndMin(v, 9, &min, &max);
printf("O menor é: %d \nO maior é: %d \n",min, max);
}
void maxAndMin(int vet[], int tam, int *min, int *max){
int i;
*max = vet[0];
*min = vet[0];
for(i = 0; i < tam; i++){
if(vet[i] < *min)
*min = vet[i];
if(vet[i] > *max)
*max = vet[i];
}
}
Ponteiros e Vetores
• Um vetor ocupa um espaço contíguo de memória do tamanho do tipo de dados do vetor multiplicado pelo número de posições de vetor
• O vetor funciona como um ponteiro que aponta para a primeira posição
• Por isso que vetores passados como argumento podem ser alterados pelas funções chamadas
Ponteiros e Vetores
• Como uma variável do tipo vetor é um endereço, é possível atribuí-la a um ponteiro. Exemplo: int a[] = {1, 2, 3, 4, 5};
int *p;
p = a;
• Após a atribuição é possível usar o ponteiro p como um vetor. Exemplo: for(i=0; i<5; i++)
p[i] = i*i;
Atenção
• Vetores ocupam endereços fixos de memória
• Não é possível atribuir um endereço a um vetor
• Exemplo errado: int a[] = {1, 2, 3, 4, 5}, b[5], i;
b = a;
• Exemplo correto: int a[] = {1, 2, 3, 4, 5}, *b, i;
b = a;
Ponteiros e Registros
#include <stdio.h>
struct Coordenada{
double x;
double y;
};
typedef struct Coordenada Coordenada;
int main(){
Coordenada c1, c2, *c3;
c3 = &c1;
...
Ponteiros e registros #include <stdio.h>
struct Coordenada{
double x;
double y;
};
typedef struct Coordenada Coordenada;
int main(){
Coordenada c1, c2, *c3;
c3 = &c1;
c1.x = -1;
c1.y = -1.5;
c2.x = 2.5;
c2.y = -5;
*c3 = c2;
printf("Coordenadas de c1: (%lf,%lf)\n",c1.x, c1.y);
}
O que será impresso na tela?
Acessando valores de um ponteiro de
registro • Duas maneiras: (*ponteiroReg).campo;
ponteiroReg->campo;
• Exemplo: Coordenada c1, *c3;
c3 = &c1;
c3->x = 1.5;
(*c3).y = 2.5;
Exemplo int main(){
Coordenada c1, c2, *c3, *c4;
c3 = &c1;
c4 = &c2;
c1.x = -1;
c1.y = -1.5;
c2.x = 2.5;
c2.y = -5;
(*c3).x = 1.5;
(*c3).y = 1.5;
c4->x = -1;
c4->y = -1;
printf("Coord. de c1: (%lf,%lf)\n",c1.x, c1.y);
printf("Coord. de c2: (%lf,%lf)\n",c2.x, c2.y);
}
O que será impresso na tela?
Exemplo • Cadastro de alimentos. O programa deve ter
opções para incluir/excluir um alimento do cadastro.
struct Food{
char nome[80];
double pesoMedio;
double calorias;
short usado;
};
typedef struct Food Food;
• Usaremos um vetor para cadastro dos alimentos.
• O campo “usado” de Food, serve para indicar se no vetor uma posição está em uso (1) ou não (0).
Exemplo - Funções
• void leFood(Food *f);
▫ Lê dados de um alimento passado como ponteiro.
• void imprimeFood(Food f);
▫ Imprime dados de um alimento. • void imprimeFoods(Food vet[], int tam);
▫ Imprime dados de um cadastro inteiro de alimentos.
• int insereFood(Food vet[], int tam, Food f);
▫ Insere um alimento no cadastro se houver espaço! • int removeFood(Food vet[], int tam, char nome[]);
▫ Remove um alimento pelo nome, se este estiver cadastrado!
Exemplo
void leFood(Food *f){
printf(" ------Lendo Alimento-------\n");
printf("Digite o nome do alimento:");
scanf("%s", f->nome);
printf("Digite o peso médio do alimento:");
scanf("%lf", &(f->pesoMedio));
printf("Digite a quantidade de calorias do alimento:");
scanf("%lf", &(f->calorias));
}
Exemplo
void imprimeFood(Food f){
printf(" \n\n--- Imprimindo Alimento ---\n");
printf("Nome: %s\n",f.nome);
printf("Peso médio: %lf\n", f.pesoMedio);
printf("Calorias: %lf\n", f.calorias);
}
void imprimeFoods(Food vet[], int tam){
int i;
for(i=0; i<tam; i++){
if(vet[i].usado == 1)
imprimeFood(vet[i]);
}
}
Exemplo
int insereFood(Food vet[], int tam, Food f){
int i;
for(i=0; i<tam; i++){
if(vet[i].usado == 0){
vet[i] = f;
vet[i].usado = 1;
return 1;
}
}
return 0; //cadastro está cheio
}
Exemplo
int removeFood(Food vet[], int tam, char nome[]){
int i;
for(i=0; i<tam; i++){
//strcmp retorna 0 se iguais
if( strcmp(vet[i].nome , nome) == 0){
vet[i].usado = 0;
return 1;
}
}
return 0; //alimento não cadastrado
}
Atividades
• Crie a função principal do cadastro de alimentos, de maneira a utilizar todas as funções apresentadas.
▫ Considere que o usuário é quem escolhe a quantidade máxima de alimentos a serem armazenados.
Atividades
• Escreva uma função que recebe um vetor de inteiros vet e retorna a média dos valores de vet e o valor mais frequente (i.e. a moda estatística)
• Exemplo: vet = {1,2,3,3,5,2,10,2,4,2}, media=3.4,
moda=2
• Escreva uma função que recebe uma string s1, contendo um nome completo e retorna o primeiro nome firstn e o último lastn.
Atividades
• Crie um programa de anotações de aula. O programa deve ter um registro do tipo: ▫ Código do tema: int ▫ Tema da aula: string ▫ Observações do aluno: string ▫ Nível de dificuldade: int (1-3), onde 1 significa
fácil, 2 - normal e 3 – difícil.
• O aluno pode cadastrar até 15 temas (registros) por disciplina. O aluno deve ter a opção de cadastrar, apagar e listar os temas cadastrados.
Atividades
• Escreva uma função “intercala” que recebe duas strings s1 e s2 e retorna uma string s3 que é a intercalação das duas primeiras
• Exemplo: ▫ s1 = “JÃSLA”, s2 = “OOIV”, s3 = “JOÃOSILVA”
• Agora faça uma nova versão de intercala que
recebe dois vetores v1 e v2 de inteiros ordenados e retorna um terceiro vetor v3 também ordenado que contém os valores de v1 e v2. Obs.: não se esqueça de verificar se o vetores estão ordenados.
• Exemplo: ▫ v1 = {1,4,9,11}, v2 = {2,3,7,10, 13}, v3 =
{1,2,3,4,7,9,10,11,13}