MC102MN

Introdução a Algoritmos e programação de computadores

Aula XIII, início de ponteiros

Ponteiros, em C, são variáveis que guardam endereços de memória de outras variáveis. Em C ponteiros são como versões mais "livres" de vetores, que te permitem fazer mais coisas com eles. Na aula de hoje veremos como usar ponteiros pra variáveis que já existem e para vetores. Mais tarde no curso veremos as aplicações realmente diferentes de ponteiros, que envolvem alocação dinâmica de memória.

Como declarar ponteiros

Um ponteiro em C é uma variável que guarda o endereço de memória de algum valor. Para declarar um ponteiro em C o que se faz é algo como:

int *ponteiro;
ou, mais formalmente,
<tipo> *<nome>;
Isso é que nem vetores, então para declarar dois ponteiros você faz
int *a, *b;
e se vc fizer
int *a, b;
apenas a é um ponteiro.

Operações com ponteiros

Com um ponteiro podemos não só olhar pra endereços de memória adjacentes, e ler ou escrever esses valores, como mudar completamente o endereço para o qual o ponteiro aponta. Formalmente, um ponteiro suporta três tipos de operação:

  • atribuição: se a e b são ponteiros, você sempre pode fazer
    a = b;
    
    e atribuir o endereço de memória apontado por b a a. Também, se a é um ponteiro e b uma variável vc pode fazer
    a = &b;
    
    e isso atribui o endereço da variável b ao ponteiro a.
  • incremento e decremento. Se a é um ponteiro,
    (a + 1)
    
    é um ponteiro para o lugar de memória onde, se a aponta para um elemento de um vetor, está o próximo elemento desse vetor. Da mesma forma,
    (a - 1)
    
    aponta para o que seria o elemento anterior. Um ponteiro pode apontar para um elemento no meio de um vetor sem problemas.
  • dereferência: se a é um ponteiro,
    *a
    
    é o valor do espaço de memória para o qual a aponta. Então se você faz
    a = b;
    
    você está guardando o valor b em a como um endereço, e se faz
    *a = b;
    
    você está guardando o valor b no endereço de memória para o qual a aponta.

Essas operações de ponteiros podem ser combinadas da forma que você quiser. Por exemplo,

*(a+2)
acessa o que seria o elemento dois depois de a num vetor.

Em C, vetores são representados internamente como ponteiros. Isso significa que se vc declara

int a[20];
e usa a numa expressão, por exemplo
funcao(a, 20, -2)
o que a expressão enxerga é um ponteiro para o primeiro elemento de a. Isso significa que você pode fazer
int *b;
e fazer
b = a;
e acessar os elementos de a sem problemas,
b[1]

Isso funciona porque internamente c representa os colchetes da seguinte forma:

n[m]
é traduzido pra
*(n+m)
antes de compilar. Isso significa que você pode escrever em C coisas como
"A casa é azul"[2]
ou
3["A casa é azul"]
ou até
3[b]

É esquisito, mas faz sentido.

Mostrar mais exemplos.

Usando ponteiros para simplificar código que já temos.

Lembram de strcat?

void strcat(char a[], char b[]) {
 int i, j;
 for (i = 0; a[i] != '\0'; ++i);
 for (j = 0, b[j] != '\0'; ++j) {
   a[i+j] = b[j]
 }
 a[i+j] = '\0';
}

Com ponteiros podemos escrever isso usando strlen e strcpy. Vamos primeiro escrever elas usando ponteiros:

int strlen(char *a) {
  int i = 0;
  for (; *a != '\0'; a++, i++);
  return i;
}

void strcpy(char *a, char *b) {
  while(*b != '\0') 
    *a++ = *b++;
}

Com essas duas, podemos escrever strcat como

void strcat(char *a, char *b) {
  strcpy(a+strlen(a), b);
}

Nos testes de mesa

Em testes de mesa é compĺexo representar ponteiros. O que normalmente se faz quando se está lidando com ponteiros é desenhar um grafo com as variáveis (e blocos alocados) como vértices e arestas apontando dos ponteiros para as variáveis para onde eles apontam.

Ponteiro vazio

Em C existe um valor específico chamado NULL (que é equivalente a 0) que é garantido que nenhum endereço nunca terá esse valor. Então, em vez de não inicializar ponteiros, normalmente atribui-se NULL aos ponteiros logo ao declará-los,

int *a = NULL;
e antes de usar um ponteiro se testa se ele é ou não NULL
if (a) { ... }

Retornar mais de um valor

Assim como com vetores, podemos usar ponteiros para retornar mais de um valor de uma função. Algo como

void eq2grau(double a, double b, double c, double *x1, double *x2) {
   double delta = b*b-4*a*c;
   *x1 = (-b+sqrt(delta))/(2*a)
   *x2 = (-b-sqrt(delta))/(2*a)
}
e para usar isso vc faz algo como
double x1, x1;
eq2grau(10, 2, 7, x1, x2);
dentro do main.

Sugestões

fazer teste de mesa usando essas funções com ponteiros. Reimplementar as funções de vetores que fizemos antes (pra somar, multiplicar por constante, produto interno) usando ponteiros explicitamente pra ver se fica mais fácil.

Footnotes:

1 FOOTNOTE DEFINITION NOT FOUND: 10

2 FOOTNOTE DEFINITION NOT FOUND: 3

Author: Alexandre Tachard Passos <alexandre.tp@gmail.com>

Date: 2010-09-21 11:50:31 BRT

HTML generated by org-mode 6.21b in emacs 23