Curso de C

Gabarito de Lista de Exercícios

 Aula 5: Ponteiros

Clique aqui para acessar a Lista 5

 

Exercício 1: Página c620.html

Enunciado:
-Explique a diferença entre

 p++;       (*p)++;        *(p++);

-O que quer dizer *(p+10);?
-Explique o que você entendeu da comparação entre ponteiros.

Solução:
- p++: incrementa o ponteiro, ou seja o endereço. Após esta instrução, o ponteiro p passará a apontar para a posição de memória imediatamente superior. Se em um vetor, o ponteiro passará a apontar a próxima posição do vetor.
- (*p)++: Incrementa o conteúdo apontado por p, ou seja, o valor armazenado na variável para qual p está apontando.
- *(p++): Incrementa p (como em p++) e acessa o valor encontrado na nova posição. Se em um vetor, esta expressão acessa o valor da posição imediatamente superior a armazenada em p antes do incremento.

- *(p+10) Acessa o valor encontrado 10 posições afrente de p. Neste caso, o apontador não é incrementado. Se em um vetor, irá acessar a décima posição após a que está sendo apontada.

- Dois ponteiros, como outras variaveis, podem ser comparados. Podemos verificar por exemplo se dois ponteiros apontam para a mesma posição de memória verificando se    p1 == p2 ou se p1 != p2
Podemos comparar se um ponteiro e 'menor' ou 'maior' que outro, ou melhor, se aponta para uma posição superior a de outro. Veja o trecho de programa como exemplo:

p1 = &vet[0];  /* vet e um vetor de 100 posicoes; p1 aponta para
                  o inicio de vet */
p2 = p1+50;    /* p2 aponta para o meio de vet */
p3 = p1+100;   /* p3 aponta para o fim de vet */

for(p=p1; p<p3; p++)
  if(p>p2)
    printf("\n\n Ultrapassamos o meio do vetor!");
 

Exercício 2: Página c630.html

Enunciado:
Fizemos a função StrCpy(). Faça uma função StrLen() e StrCat() que funcionem como as funções strlen() e strcat() de string.h, respectivamente.

Solução:
/* Exemplo de funcao StrLen ---------------------------- */
#include <stdio.h>

StrLen (char *str)
{
int tamanho = 0;
while (*str)
{
  tamanho++;
  str++;
}
return tamanho;
}

main ()
{
int t;
char str1[100];
printf ("\n\nEntre com uma string: \n");
gets (str1);
t = StrLen(str1);
printf ("\n Voce digitou \n%s\ncom %d letras.",str1,t);
}

Comentários:
A função StrLen percorre o vetor procurando o seu fim. A cada iteração que o fim do vetor não é encontrado, a variável tamanho é incrementada. Quando o fim é encontrado, a variável tamanho possui o número de iterações usadas até chegar ao fim, ou seja, o tamanho da string.

/* Exemplo de funcao StrCat --------------------------- */
#include <stdio.h>
#include <string.h>

StrCat (char *primeira, char *segunda)
{
char *p;
/* --->p aponta para o final da primeira string */
p = primeira+strlen(primeira);
while (*segunda)
{
  *p = *segunda;
  p++;
  segunda++;
}
*p = '\0';
}

main ()
{
char str1[100], str2[100], str3[200];
printf ("\n\nEntre com uma string: \n");
gets (str1);
printf ("\n\nEntre com uma string: \n");
gets (str2);
str3[0] = '\0';
StrCat(str3, str1);
StrCat(str3, str2);
printf ("\n Voce digitou \n%s",str3);
}

Comentários:
O vetor p é apontado para o final da primeira string.Em seguida, cada posição da segunda string é copiada para o endereço p, que por sua vez é incrementado a
cada iteração. Desta forma, a segunda string é copiada para o final da primeira, e a primeira se torna uma string maior, com o conteúdo das duas strings.
 

Exercício 3: Página c640.html

Enunciado:
Escreva a função int strend(char *s, char *t) que retorna 1 (um) se a cadeia de caracteres 't' ocorrer no final da cadeia 's', e 0 (zero) caso contrário.

Solução:
/* Exemplo de funcao strend ---------------- */
#include <stdio.h>
#include <string.h>

strend (char *str, char *t)
{
char *p;
p = str + strlen(str) - strlen(t);
while (*t)
{
  if (*p != *t) return 0;
  p++;
  t++;
}
return 1;
}

main ()
{
char str[100], t[20];
printf ("\n\nEntre com uma string: \n");
gets (str);
printf ("\n\nEntre com a expressao de teste:\n");
gets (t);
if (strend(str, t))
  printf ("\n A expressao ocorre no final da frase.\n");
  else printf ("\n A expressao NAO ocorre no final da frase.\n");
}

Comentários:
Primeiro, o apontador p é apontado para uma posição que será o final da primeira string subtraído do tamanho da string t. A partir daí, cada posição é comparada, até o final da string t. Se ocorrer alguma posição diferente, a função retorna um 0 imediatamente, sem comparar o restante. Caso a função termine o loop, significa que o trecho é realmente igual e a função retorna o flag 1.
 

Exercício 4: Página c660.html

Enunciado:
Escreva um programa que declare uma matriz 100x100 de inteiros. Você deve inicializar a matriz com zeros usando ponteiros. Preencha depois a matriz com os números de 1 a 10.000 usando ponteiros.

Solução:
/* Problema das matrizes ---------------- */
#include <stdio.h>
#define N 100

main ()
{
int mat[N][N];
int *p;
int i, j, soma = 0;

p = &mat[0][0];   /* Inicializa o ponteiro no inicio da matri */

/* Inicializando a matriz com zeros.. */
for (i=0; i<N; i++)
  for (j=0; j<N; j++)
    {
    *p = 0;
    p++;
    }

/* Preenchendo a matriz com numeros */
p = &mat[0][0];
for (i=0; i<N; i++)
  for (j=0; j<N; j++)
    {
    *p = soma;
    soma++;
    p++;
    }
}

Comentários:
Pode parecer um pouco inútil este tipo de exercício, mas apesar de não se ter um objetivo claro (uma resposta na tela) ele é útil para desenvolver a capacidade do aluno de trabalhar e resolver problemas usando ponteiros. O exercício aponta o ponteiro p para o início da matriz e percorre toda ela preenchendo-a com zeros. Em seguida, p é novamente apontado para o início (se não fizer isto, não funciona) da matriz e faz-se então o preenchimento da matriz com os números, usando-se uma variável auxiliar.
 

Exercícios de Fixação:

Exercício 1:
(V) Qualquer expressão de vetor e índice é equivalente a uma outra expressão escrita com um apontador e um deslocamento
(V) Após a atribuição  pa=&a[0];  pa e a possuem valores idênticos
(V) A atribuição  pa=&a[0];  pode ser escrita como pa=a;
(V) Uma referência a a[i] pode ser escrita como *(a+i)
(V) &a[i] e a+i são idênticos
(V) a+i é o endereço do i-ésimo elemento após a
(V) pa[i] é idêntico a *(pa+i)
(V) pa=a é uma operação válida
(V) pa++ é uma operação válida
(F) a=pa é uma operação válida=> o nome de um vetor não pode ser usado como uma variável
(F) a++  é uma operação válida=> o nome de um vetor não pode ser usado como uma variável

Comentários:
- Se pa aponta para um elemento particular de um vetor, então, por definição, pa+1 aponta para o próximo elemento, pa+i aponta para i elementos após pa e pa-i aponta para i elementos antes de pa. Assim, se pa aponta para a[0], *(pa+1) refere-se ao conteudo de a[1], pa+i é o endereço de a[i] e *(pa+i) é o conteúdo de a[i].
- Estas observações aplicam-se independentemente do tipo ou tamanho das variáveis no vetor a.
- A correspondência entre indexação e aritmética com ponteiros é evidentemente muito estreita. Por definição, o valor de uma variável ou expressão do tipo vetor é o endereço do elemento zero do vetor. Assim, após a atribuição pa=&a[0]; pa e a possuem valores idênticos. Como o nome de um vetor é sinônimo para o local do elemento inicial, a atribuicao pa=&a[0] também pode ser escrita como pa=a;
- a[i] pode ser escrita como *(a+i). Na avaliação de a[i], o C a converte para *(a+i) imediatamente; as duas formas são equivalentes. Aplicando-se o operador & a ambas as partes dessa equivalência, segue-se que &a[i] e a+i também são idênticos: a+i é o endereço do i-esimo elemento após a. Por outro lado, se pa é um apontador, expressões podem usá-lo como um subscrito; pa[i] é idêntico a *(pa+i). Em suma, qualquer expressão de vetor e indice é equivalente a uma escrita com um apontador e um deslocamento.
- Existe uma diferença importante entre o nome de um vetor e um apontador que deve ser sempre lembrada: um apontador é uma variavel, de forma que pa=a e pa++ são operações válidas. Mas o nome de um vetor não é uma variável (poderia ser visto como um ponteiro constante, mas não uma variável); construções como a=pa e a++ são ilegais.
 

Exercício 2:
O que está errado com os programas abaixos? Descubra e indique a solução para consertá-los. Execute-o no computador para ver se o erro foi resolvido.
a)
void main(void) /* esse programa esta errado */
{
int x, *p;
x = 10;
*p = x;
}

Obs: por se tratar de um programa muito pequeno, talvez a execução deste não mostrará de imediato o erro. Mas ele existe e é sério.

Solução:
Este programa atribui o valor 10 a alguma posição de memoria desconhecida. O ponteiro p nunca recebeu um valor; portanto, ele contém lixo. Esse tipo de problema sempre passa despercebido, quando o programa é pequeno, porque as probabilidades estão a favor de que p contenha um endereço "seguro" - um endereço que não esteja em seu código, área de dados ou sistema operacional. Contudo, a medida que seu programa cresce, a probabilidade de p apontar para algo vital aumenta. Eventualmente, seu programa pára de funcionar. A solução é sempre ter certeza de que um ponteiro está apontando para algo válido antes de usá-lo.

b)
void main(void) /* esse programa esta errado */
{
int x, *p;
x = 10;
p = x;
printf ("%d", *p);
}

Solução comentada:
- O erro aqui apresentado é provocado por um simples equívoco sobre como usar um ponteiro. A chamada de printf() não imprime o valor de x, que é 10. Imprime um valor desconhecido porque a atribuicao p=x; está errada. Esse comando atribui o valor 10 ao ponteiro p, que se supõe conter um endereço, não um valor. Para corrigí-lo, basta escrever p=&x;
 

Exercício 3:
Qual o valor de y no final do programa? Tente primeiro descobrir fazendo um teste de mesa e depois verifique no computador o resultado. A seguir, escreva um /* comentário */ em cada comando de atribuição explicando o que ele faz e o valor da variavel à esquerda do '=' após sua execução.

Solução:
O valor de y é 4, e o programa comentado fica:

main()
{
int y, *p, x;
y = 0;       /* inicializa y com 0 => y=0 */
p = &y;      /* atribui o endereco de y ao ponteiro p
                p contem o endereco de y (ex:DS:FFF4)*/
x = *p;      /* atribui o conteudo de onde p aponta
                (valor de y) para x, que passa a valer 0 */
x = 4;       /* atribui 4 a x */
(*p)++;      /* incrementa de 1 o conteudo de onde p aponta,
                alterando o valor de y para 1 */
x--;         /* decrementa 1 de x => x = 3 */
(*p) += x;   /* adiciona x ao conteudo de onde p aponta,
                alterando o valor de y para 4 */
printf ("y = %d\n", y); /* imprime "y = 4" */
}
 

Exercício 4:
Verifique o programa abaixo. Encontre o seu erro e corrija-o para que escreva o numero 10 na tela.

#include <stdio.h>
main()
{
int x, *p, **q;
p = &x;
q = &p;
x = 10;
printf("\n%d\n", &q);
}

Solução:
O programa contém um erro na linha do printf, onde ele manda imprimir o endereço de q (&q). Na realidade, para se imprimir o valor 10 (valor de x) deve-se imprimir o valor apontado pelo valor apontado por q. Veja o esquema:
   x = 10;
p aponta para x;
                     /* valor apontado por p: 10 */
                     /* conteudo de p: &x */
q aponta para p;
                     /* valor apontado por q: &x que e o conteudo do ponteiro p */
                     /* conteudo de q: &p */

Tente refletir nisto alguns isntantes... É meio complicado.
 

Exercício 5:
Reescreva o exemplo da página c510.html alterando a forma de varrer o loop. Ao invés de você usar os índices do array, você deverá usar um ponteiro e ir incrementando o ponteiro para atingir cada posição.

Solução:
#include <stdio.h>
main ()
{
int num[100];     /* Declara um vetor de 100 posicoes */
int *p;

p = &num[0];
do {
     printf ("\nEntre com um numero (-999 p/ terminar): ");
     scanf ("%d",p);
     p++;
   } while (*(p-1)!=-999);

printf ("\n\n\n\t Os numeros que voce digitou foram:\n\n");
for (p=num; *p!=-999; p++)
  printf (" %d\n",*p);
}
 

Exercicio 6:
Fazer um programa que implemente a multiplicação entre duas matrizes (lista 4), armazenando o resultado numa terceira matriz.A implementação deverá ser feita usando-se a aritmética de ponteiros.

Solução:

/* Multiplicacao de Matrizes - versao 2.0 ---------- */
/* ------------------------------------------------- */
#include <stdio.h>
#define TAM 10
main()
{
int mat1[TAM][TAM],   /* As matrizes */
    mat2[TAM][TAM],
    mat3[TAM][TAM];
int d1x, d1y,
    d2x, d2y,  /* As dimensoes das matrizes */
    d3x, d3y;

int i, j, k;  /* Contadores auxiliares */
int *p, *q;   /* Apontadores auxiliares */

do
  {
  /* Leitura das dimensoes: */
  printf("\nEntre com as dimensoes da primeira matriz: ");
  scanf("%d%d", &d1x, &d1y);
  printf("\nEntre com as dimensoes da segunda matriz: ");
  scanf("%d%d", &d2x, &d2y);
  }while (d1y != d2x);

/* Leitura das matrizes: */
printf("\n Matriz 1:\n");
for (i=0; i<d1x; i++)
   {
   p = &mat1[i][0];
   for (j=0; j<d1y; j++)
     {
     printf("\nposicao (%d,%d): ", i, j);
     scanf("%d",p);
     p++;
     }
   }

printf("\n Matriz 2:\n");
for (i=0; i<d2x; i++)
   {
   p = &mat2[i][0];
   for (j=0; j<d2y; j++)
     {
     printf("\nposicao (%d,%d): ", i, j);
     scanf("%d",p);
     p++;
     }
   }

/* Impressao das matrizes */
printf("\n\nPrimeira Matriz:");
for (i=0; i<d1x; i++)
   {
   p = &mat1[i][0];
   printf("\n");
   for (j=0; j<d1y; j++)
     printf("\t%d", *p++);
   }

printf("\n\nSegunda Matriz:");
for (i=0; i<d2x; i++)
   {
   p = &mat2[i][0];
   printf("\n");
   for (j=0; j<d2y; j++)
     printf("\t%d", *p++);
   }

/*zera a matriz produto */
d3x = d1x;
d3y = d2y;
for (i=0; i<d3x; i++)
  {
  p = &mat3[i][0];
  for (j=0; j<d3y; j++)
    {
    *p = 0;
    p++;
    }
  }

/* Calculo da matriz produto */
for (i=0; i<d3x; i++)
  for (j=0; j<d3y; j++)
    {
    p = &mat1[i][0];
    q = &mat2[0][j];
    for (k=0; k<d2x; k++)
      {
      mat3[i][j] += (*p)*(*q);
      p++;
      q += TAM;
      }
    }

/* Imprime o resultado */
printf("\n\nMatriz Resultado:");
for (i=0; i<d3x; i++)
   {
   p = &mat3[i][0];
   printf("\n");
   for (j=0; j<d3y; j++)
     printf("\t%d", *p++);
   }

printf("\n");
}

Obs: O programa é praticamente 'impossível' de ser entendido para alguém que não tenha a versão 1.0, preparado para o exercício da aula passada. Caso você tenha dificuldades de entendê-lo, procure o gabarito da aula 4, e compare o exercício. Caso a dúvida persista, comentaremos na lista.
 

Exercicio 7:
Refaçaa o programa que ordena um vetor de inteiros de N posições (lista 4). Para isto você precisará usar um loop. Verifique o número de iterações até que o programa consiga finalmente ordenar o vetor (sugestões: use valores superiores para N - 25 ou 35).

Solução:
/* Ordena vetores: Versao 2.0 --------------- */
/* ------------------------------------------ */
#include <stdio.h>
#define N 20
main()
{
int vet[N] = { 3, 5, 2, 9, 5, 7, 1, 3, 2, 0,
               12, 13, 15, 6, 4, 2, 8, 7, 5, 4};
int i, *p, *fim, *menor, aux;

fim = &vet[N-1];
for (i=0; i<N; i++)
  {
   menor = p = &vet[i];
   while(p<=fim)
     {
     if (*p < *menor)
       menor = p;
     p++;
     }
   aux = *menor;
   *menor = vet[i];
   vet[i] = aux;
  }

printf("\n\nResultado:\n");
for (p=&vet[0]; p<=fim; p++)
  printf("%d\n", *p);
}

Comentários:
Para uma melhor compreensão do programa, sugere-se que os alunos desenvolvam o programa passo a passo, seguindo cada atribuição, com o auxilio de lápis e papel. A idéia principal é que o ponteiro menor aponte para a menor posição do vetor. Troca-se então este valor pelo do início do vetor, e uma nova busca é
feita no restante do vetor. O resultado é guardado na segunda posição, e assim segue, até que todas as posições estejam tomadas por valores, que serão então crescentes.
 

Exercicio 8:
Faça um programa que leia uma string do teclado. Em seguida ele verifica se a string corresponde a um número (como "-987.234"). Se sim, o programa deverá calcular o valor que a string representa (inlcusive negativo) e apresentá-lo na tela como float. Se não (caso a string não seja correspondete a um número) o número apresentado deverá ser zero. Utilize-se de ponteiros para percorrer a string.

Obs: Sobre bibliotecas, para quem conhece outras bibliotecas que não estão sendo usadas com frequência no nosso curso: Você deverá fazer este exercício apenas com as bibliotecas stdio.h, string.h e ctype.h. As funções da biblioteca stdlib.h não deverão ser usadas.

Solução:
/* Conversao de string para float ---------- */
/* Versao 1.0 ------------------------------ */
#include <stdio.h>
#include <ctype.h>

main()
{
char buf[30], *p;
float res = 0;
int sinal = 1, pot;

printf("\n\nEntre com a string:\n");
scanf("%s", buf);

p = &buf[0];

/* Retira possiveis espacos no inicio da string */
while ( (!isdigit(*p)) && (*p != '\0') )
  {
  if (*p == '-')
    sinal = -1;
  p++;
  }

/* Parte inteira */
for (res=0.0; isdigit(*p); p++)
   res = 10.0 * res + (*p - '0');
if (*p == '.')
   p++;

/* Parte decimal */
for (pot = 1.0; isdigit(*p); p++)
   {
   res = 10.0 * res + (*p - '0');
   pot *= 10.0;
   }

/* Apresentacao do resultado */
res = sinal*res/pot;
printf("\nVeja o resultado: %f\n",res);
}

Comentários:
- a partir do início da string, a função desconsidera os brancos, examina o sinal + ou - (se existir); a seguir, espera por dígitos e, enquanto estes ocorrerem,
vai fazendo o cálculo do valor numérico correspondente. (ex: se for lido um '2' seguido de '8' e de '1', realiza o calculo ((2*10.)+8)*10.+1);
se então ocorrer um ponto decimal, este será pulado e o valor numérico seguinte será calculado seguindo o mesmo princípio anterior, a menos da potência, que vai sendo acumulada.
Ao final da rotina, o valor será multiplicado pelo sinal e dividido pela potência obtida, chegando ao valor numérico correspondente a string.
 

Exercicio 9:
Refaça o programa da página c530.html, ordenando as strings lidas por tamanho (menores primeiro, maiores depois). Use um vetor auxiliar com um ponteiro para cada string.

Solução:
/* Ordena Strings: Versao 2.0 --------------- */
/* ------------------------------------------ */
#include <stdio.h>
#include <string.h>
#define N 5
#define TAM 30
main()
{
char str[N][TAM];
char* ap[N];
int vet1[N], vet2[N];
int *fim, *menor, *q;
int i, j;

/* Leitura das strings: */
printf("\n\nEntre com %d Strings:\n", N);
for (i=0; i<N; i++)
  {
  scanf("%s",&str[i][0]);
  vet1[i] = strlen(str[i]);
  }

/* Ordena as strings propriamente */
fim = &vet1[N-1];
for (i=0; i<N; i++)
  {
   for(menor=q=&vet1[0]; q<=fim; q++)
     if (*q < *menor)
       menor = q;

   j = menor-&vet1[i];
   *menor = 50;    /* Maior que o maximo... */
   ap[i] = &str[i+j][0];
  }

printf("\nConjunto de strings no final:\n");
for (i=0; i<N; i++)
  printf("%s\n", ap[i]);
}

Comentários:
Este programa usa boa parte do exercício 2, na rotina de ordenação, mas a ordenação em si, neste exemplo é mais simples. Na realidade este é o mesmo exercício, porém mais elaborado. Entendendo o outro exercício bem, não será difícil entender este.

  Sumário

Dúvidas? Dê uma olhadinha em nosso FAQ

Perguntas para a lista do Curso de C
Curso de C do CPDEE/UFMG - 1996-1998