Gabarito da Lista 5


Módulo 5:  Aula 6: Ponteiros

Exercício: Página c620.html

Enunciado:

(a) -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 a frente 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.

(b) Enunciado

Qual o valor de y no final do programa? Tente primeiro descobrir  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 variável à esquerda do '=' após sua execução.

        int main()
        {
           int y, *p, x;
           y = 0;
           p = &y;
           x = *p;
           x = 4;
           (*p)++;
           x--;
           (*p) += x;
           printf ("y = %d\n", y);
                           return(0);
                   }

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

int main()
{
int y, *p, x;
y = 0;       /* atribui o valor 0 a y => 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: 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';
}

int main ()
{
    char str1[100], str2[1003], 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);
          return(0);
}

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: 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); /* p aponta para o final da string str - tamanho de t */
    while (*t)
    {
      if (*p != *t) return 0;
      p++;
      t++;
    }
    return 1;
}

int 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");
          return(0);
}

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: Página c650.html

Verifique o programa abaixo. Encontre o seu erro e corrija-o para que escreva o numero 10 na tela.

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

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;
 
q aponta para p;
 
==> *q  é igual a p ; como *p  é igual a x, basta escrever *(*q) para se ter x. Logo, o printf ficaria:

printf("\n%d\n", **q); 

 

Exercício : 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 matriz */

/* 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 adicionais:

Exercício 1:
Aprendemos, pelo curso, que o valor de uma variável ou expressão do tipo vetor é o endereço do elemento zero do vetor. Seja a[] um vetor qualquer, independente de tipo e tamanho, e pa um ponteiro para o mesmo tipo de a[]. Responda V ou F, justificando:

(V) Após a atribuição  pa=&a[0]pa e a possuem valores idênticos, isto é, apontam para o mesmo endereço
(V) A atribuição  pa=&a[0];  pode ser escrita como pa=a;
(V) a[i] pode ser escrito como *(a+i)
(V) &a[i] e a+i são idênticos
(V) a+i e' o endereço do i-ésimo elemento após a
(V) pa[i] e' idêntico a *(pa+i)
(V) pa=a e' uma operação valida
(V) pa++ e' uma operação valida
(F) a=pa e' uma operação valida ==> Um vetor não pode ter seu endereço modificado
(F) a++  e' uma operação valida ==> Um vetor não pode ter seu endereço modificado
 

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;


Curso de C do CPDEE/UFMG - 1996-1999