Ponteiros em C: mudanças entre as edições

De Wiki Cursos IFPR Foz
Ir para navegaçãoIr para pesquisar
 
(21 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 26: Linha 26:
  y = *ip + 1    /*pega o que ip aponta e soma 1 e atribui a y*/
  y = *ip + 1    /*pega o que ip aponta e soma 1 e atribui a y*/
  *ip += 1        /*Incrementa *ip de 1*/
  *ip += 1        /*Incrementa *ip de 1*/
</syntaxhighlight>
;Exibindo o endereço de uma variável: Programa exemplo para exibir o endereço de uma variável:
<syntaxhighlight lang="c">
#include <stdio.h>
int main()
  {
    char letra = 's';
    int  numero = 35;
    printf("Exibindo o endereço de memória de variáveis:\n\n");
    printf("Variável letra  = %c, endereço = %p\n",letra, &letra);
    printf("Variável numero = %d, endereço = %p\n",numero,&numero);
  }
</syntaxhighlight>
</syntaxhighlight>


Linha 31: Linha 44:
A linguagem C passa '''argumentos''' para funções '''por valor'''. Portanto, não há como acessar diretamente as variáveis usadas para chamar a função. Para acessar em uma função as variáveis fornecidas como argumentos deve-se utilizar '''ponteiros''', que apontam para as variáveis.
A linguagem C passa '''argumentos''' para funções '''por valor'''. Portanto, não há como acessar diretamente as variáveis usadas para chamar a função. Para acessar em uma função as variáveis fornecidas como argumentos deve-se utilizar '''ponteiros''', que apontam para as variáveis.


;Exemplo 1: Função que modifica uma variável do programa principal<ref name="TENENBAUM">TENENBAUM, Aaron M.; LANGSAM, Yedidyah; AUGENSTEIN, Moshe. '''Estruturas de dados usando C'''. Makron Books, 1995.</ref>:
;Exemplo 1: Função que modifica uma variável do programa principal, recebendo '''argumentos por referência''' através de um '''ponteiro'''<ref name="TENENBAUM">TENENBAUM, Aaron M.; LANGSAM, Yedidyah; AUGENSTEIN, Moshe. '''Estruturas de dados usando C'''. Makron Books, 1995.</ref>:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
  #include <stdio.h>
  #include <stdio.h>
Linha 46: Linha 59:
   ++*y;
   ++*y;
   printf("Valor de y=%d\n", *y);
   printf("Valor de y=%d\n", *y);
  } /* fim Func */
  } // fim Func
</syntaxhighlight>
 
:Veja a diferença com uma função que recebe '''argumento por valor''':
<syntaxhighlight lang="c">
#include <stdio.h>
void Func(int y);                      /*Protótipo da função*/
void main ()                            /*Programa principal para teste*/
{
  int x=15;
  printf("Primeiro valor de x=%d\n", x);
  Func(x);
  printf("Segundo valor de x=%d\n", x);
}
void Func(int y)                      /*Definição da função*/
{
  ++y;
  printf("Valor de y=%d\n", y);
} // fim Func
</syntaxhighlight>
</syntaxhighlight>


Linha 112: Linha 143:
</syntaxhighlight>
</syntaxhighlight>


<!--
;Diferença entre o nome de um vetor e um ponteiro: Um ponteiro é uma variável, por exemplo, se <tt>pv</tt> é um ponteiro e <tt>v</tt> é um vetor, <tt>pv=v</tt> e <tt>pv++</tt> são operações válidas. Entretanto, o nome do vetor não é uma variável, portanto, a operação <tt>v++</tt> é ilegal.
;Diferença entre o nome de um vetor e um ponteiro: Um ponteiro é uma variável, por exemplo, se <tt>pv</tt> é um ponteiro e <tt>v</tt> é um vetor, <tt>pv=v</tt> e <tt>pv++</tt> são operações válidas. Entretanto, o nome do vetor não é uma variável, portanto, a operação <tt>v++</tt> é ilegal.
-->


===Vetores como parâmetros===
===Vetores como parâmetros de funções===


Todo parâmetro deve ser declarado dentro da função. Entretanto, a faixa valores de um parâmetro vetor somente é declarada no programa principal (ou chamador da função)  <ref name="TENENBAUM"/>. Isto ocorre porque o C não aloca novamente memória para o parâmetro, usando o vetor original do programa principal.
Todo parâmetro deve ser declarado dentro de uma função. Entretanto, a '''faixa valores''' de um '''parâmetro vetor''' somente é declarada no programa principal (ou chamador da função)  <ref name="TENENBAUM"/>. Isto ocorre porque o C não aloca novamente memória para o parâmetro, usando o vetor original do programa principal.


;Exemplo de vetor passado como argumento: Função que calcula a média dos valores de um vetor.
;Exemplo de vetor como parâmetro: Função que calcula a média dos valores de um vetor.
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
#include <stdio.h>
#include <stdio.h>
Linha 130: Linha 159:
   int i;
   int i;
   float soma = 0;
   float soma = 0;
   for (i=0; i != TAM; i++)
   for (i=0; i < tamanho; i++)
     soma += vetor[i];
     soma += vetor[i];
   return (soma / TAM);
   return (soma / tamanho);
}
}
void main ()
void main ()
Linha 143: Linha 172:
</syntaxhighlight>
</syntaxhighlight>


Observe que, como uma variável '''vetor é um ponteiro''', os '''argumentos vetores''' são passados por '''referência''', e não por valor como variáveis simples.
===Vetores como argumentos por referência para funções===
 
Como uma variável '''vetor é um ponteiro''' os '''argumentos vetores''' são passados por '''referência''' para funções, e não por valor como variáveis simples. O que é passado para a função é o '''endereço base''' do vetor.
 
;Exemplo de vetor como argumento por referência:
 
<syntaxhighlight lang="c">
#include <stdio.h>
#define TAM 5
void Func(vetor, tamanho)
int *vetor; //Esta declaração é equivalente a int vetor[]; 
int tamanho;
{
  int i;
  for (i=0; i < tamanho; i++)
    vetor[i]=99;
}
void main ()
{
  int i;
  int v[TAM]={1, 2, 3, 4, 5};
  for (i=0; i < TAM; i++)
    printf("Vetor1[%d]= %d\n", i, v[i]);
  Func(v, TAM);
  for (i=0; i < TAM; i++)
    printf("Vetor2[%d]= %d\n", i, v[i]);
}
</syntaxhighlight>
 
:Note que a declaração formal do parâmetro vetor pode ser declarada de duas formas:
int *vetor;
:ou
int vetor[];
:as duas são equivalentes, mas a primeira é preferível por explicitar que a variável é um ponteiro.


==Ponteiros e strings==
==Ponteiros e strings==
Linha 167: Linha 229:
  }
  }
</syntaxhighlight>
</syntaxhighlight>
:Note que o ponteiro para a string <tt>c</tt> (equivalente a <tt>&v[0]</tt>) é passado como argumento para a função <tt>Strlen</tt>, a qual é copiada para o parâmetro <tt>*s</tt>. Portanto, internamente a função é possível incrementar a variável ponteiro <tt>s</tt> com <tt>s++</tt>.
:Note que o ponteiro para a string <tt>c</tt> (equivalente a <tt>&c[0]</tt>) é passado como argumento para a função <tt>Strlen</tt>, a qual é copiada para o parâmetro <tt>*s</tt>. Portanto, internamente a função é possível incrementar a variável ponteiro <tt>s</tt> com <tt>s++</tt>.


:Compare com a função Strlen sem ponteiros vista no estudo de [[Caracteres e String em C | Strings]].
:Compare com a função Strlen sem ponteiros vista no estudo de [[Caracteres e String em C | Strings]].
Linha 186: Linha 248:
   for (; *s1 != '\0'; s1++)
   for (; *s1 != '\0'; s1++)
     ;
     ;
  for (; *s2 != '\0'; s1++, s2++)
    *s1=*s2;
}
</syntaxhighlight>
:Detalhe do funcionamento da função:
:*O segundo '''for''' poderia ser escrito da forma:
   for (; *s2 != '\0'; *s1++=*s2++)
   for (; *s2 != '\0'; *s1++=*s2++)
     ;
     ;
}
:neste caso, o valor apontado por s1 é primeiro igualado ao apontado por s2 e depois os ponteiros s1 e s2 são incrementados.
</syntaxhighlight>
;Detalhe do funcionamento da função:No segundo <tt>for</tt> o valor apontado por s1 é igualado ao apontado por s2 e depois os ponteiros s1 e s2 são incrementados.


:Compare com a função Strcat sem ponteiros vista no estudo de [[Caracteres e String em C | Strings]].
:Compare com a função Strcat sem ponteiros vista no estudo de [[Caracteres e String em C | Strings]].


===Exercícios com ponteiros e strings===
Veja referência interessante sobre ponteiros<ref>http://homepages.dcc.ufmg.br/~joaoreis/Site%20de%20tutoriais/c_int/ponteiros.htm</ref>.
#Construa uma função que receba como parâmetro um ponteiro para uma string e informe quantos caracteres 'a' tem o string.


==Referências==
==Referências==
Linha 201: Linha 266:


----
----
--[[Usuário:Evandro.cantu|Evandro.cantu]] ([[Usuário Discussão:Evandro.cantu|discussão]]) 10h31min de 12 de junho de 2014 (BRT)
--[[Usuário:Evandro.cantu|Evandro.cantu]] ([[Usuário Discussão:Evandro.cantu|discussão]]) 12h44min de 29 de agosto de 2014 (BRT)
----
----


[[Categoria:Estruturas de Dados]]
[[Categoria:Estruturas de Dados]]

Edição atual tal como às 20h20min de 10 de outubro de 2014

Ponteiros em C

Um ponteiro é uma variável que contem o endereço de uma variável.

O operador & fornece o endereço de uma variável. Por exemplo, de c é um char e p é um ponteiro,

 p=&c

atribui a p o endereço da variável c. Diz-se, portanto, que "p aponta para c".

O operador * quando aplicado a um ponteiro acessa o objeto apontado por ele. Por exemplo, suponha que x e y são inteiros e ip é um ponteiro para int. A sequência abaixo mostra como usar & and *[1]:

 int x=1, y=2;
 int *ip;     /* ip é um ponteiro para int */
 ip = &x;     /* ip agora aponta para x */
 y = *ip;     /* y agora é 1 */
 *ip = 0;     /* x agora é 0 */

Na declaração de variáveis ponteiro, cada ponteiro aponta para o tipo de dado declarado, se comportando como tal.

Por exemplo, se ip aponta para o inteiro x, então *ip pode ocorrer no contexto que x ocorre, portanto,

 *ip = *ip + 10; /*Incrementa *ip de 10 (ou incrementa x de 10)*/
 y = *ip + 1     /*pega o que ip aponta e soma 1 e atribui a y*/
 *ip += 1        /*Incrementa *ip de 1*/
Exibindo o endereço de uma variável
Programa exemplo para exibir o endereço de uma variável:
#include <stdio.h>
int main()
  {
    char letra = 's';
    int  numero = 35;
    printf("Exibindo o endereço de memória de variáveis:\n\n");
    printf("Variável letra  = %c, endereço = %p\n",letra, &letra);
    printf("Variável numero = %d, endereço = %p\n",numero,&numero);
  }

Ponteiros e argumentos de funções

A linguagem C passa argumentos para funções por valor. Portanto, não há como acessar diretamente as variáveis usadas para chamar a função. Para acessar em uma função as variáveis fornecidas como argumentos deve-se utilizar ponteiros, que apontam para as variáveis.

Exemplo 1
Função que modifica uma variável do programa principal, recebendo argumentos por referência através de um ponteiro[2]:
 #include <stdio.h>
 void Func(int *y);                      /*Protótipo da função*/
 void main ()                            /*Programa principal para teste*/
 {
   int x=15;
   printf("Primeiro valor de x=%d\n", x);
   Func(&x);
   printf("Segundo valor de x=%d\n", x);
 }
 void Func(int *y)                       /*Definição da função*/
 {
   ++*y;
   printf("Valor de y=%d\n", *y);
 } // fim Func
Veja a diferença com uma função que recebe argumento por valor:
 #include <stdio.h>
 void Func(int y);                      /*Protótipo da função*/
 void main ()                            /*Programa principal para teste*/
 {
   int x=15;
   printf("Primeiro valor de x=%d\n", x);
   Func(x);
   printf("Segundo valor de x=%d\n", x);
 }
 void Func(int y)                       /*Definição da função*/
 {
   ++y;
   printf("Valor de y=%d\n", y);
 } // fim Func
Exemplo 2
Função Swap que faz intercâmbio do valor de duas variáveis[1]:
 #include <stdio.h>
 void Swap(int *px, int *py);            /*Protótipo da função*/
 void main ()                            /*Programa principal para teste*/
 {
   int a=1, b=2;
   printf("Valor de a=%d, valor de b=%d\n", a, b);
   Swap(&a, &b);
   printf("Valor de a=%d, valor de b=%d\n", a, b);
 }
 void Swap(int *px, int *py)             /*Definição da função*/
 {
   int temp;
   temp = *px;
   *px = *py;
   *py = temp;
 }

Como o operador & fornece o endereço da variável, &a aponta para a. Na função Swap os parâmetros devem ser declarados como ponteiros e os argumentos são acessados por eles.

Ponteiros e vetores

Há uma forte relação entre ponteiros e vetores.

Na linguagem C uma variável vetor é implementada como uma variável ponteiro.

Quando declaramos um vetor, por exemplo:

 int v[10];

a variável v é "um ponteiro para int", ou int *v. Não aparece um asterisco na declaração porque os colchetes indicam automaticamente que a variável é um ponteiro. A diferença entre as declarações int *v; e int v[10]; é que a última reserva 10 posições de inteiros começando na posição v[0][2].

A variável v guarda o endereço base do vetor, que é o endereço do dado armazenado na posição v[0] do vetor. Isto é equivalente a definir um ponteiro, por exemplo pv e atribuir a ele o endereço de v[0]:

 pv=&v[0];
 //A expressão acima é equivalente a:
 pv=v;
 //uma vez que v guarda o endereço inicial do vetor,

Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do mesmo tipo para o qual o ponteiro aponta. Se você incrementa um ponteiro char* ele anda 1 byte na memória, se você incrementa um ponteiro int* ele anda 2 bytes na memória.

No caso de vetores, pode-se acesssar os valores dos dados armazenados no vetor utilizando ponteiros.

Equivalência entre índices de vetores e ponteiros
Suponha que você defina uma variável i como índice para acessar os valores em um vetor v, logo:
 v[i]
é equivalente a utilizar o ponteiro
 *(v + i)
Exemplo:
 #include <stdio.h>
 void main ()
 {
   int v[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   printf("O terceiro elemento do vetor é: %d\n", v[2]);
   printf("O quarto elemento do vetor é: %d\n", *(v+3));
 }
Diferença entre o nome de um vetor e um ponteiro
Um ponteiro é uma variável, por exemplo, se pv é um ponteiro e v é um vetor, pv=v e pv++ são operações válidas. Entretanto, o nome do vetor não é uma variável, portanto, a operação v++ é ilegal.

Vetores como parâmetros de funções

Todo parâmetro deve ser declarado dentro de uma função. Entretanto, a faixa valores de um parâmetro vetor somente é declarada no programa principal (ou chamador da função) [2]. Isto ocorre porque o C não aloca novamente memória para o parâmetro, usando o vetor original do programa principal.

Exemplo de vetor como parâmetro
Função que calcula a média dos valores de um vetor.
#include <stdio.h>
#define TAM 5
int Media(vetor, tamanho)
float vetor[]; //Note que não é declarada a faixa de valores do vetor.
int tamanho;
{
  int i;
  float soma = 0;
  for (i=0; i < tamanho; i++)
     soma += vetor[i];
  return (soma / tamanho);
}
void main ()
{
  float Med;
  float v[TAM]={1.0, 2.0, 3.0, 4.0, 5.0};
  Med = Media(v, TAM);
  printf("Média do vetor: %f\n", Med);
}

Vetores como argumentos por referência para funções

Como uma variável vetor é um ponteiro os argumentos vetores são passados por referência para funções, e não por valor como variáveis simples. O que é passado para a função é o endereço base do vetor.

Exemplo de vetor como argumento por referência
#include <stdio.h>
#define TAM 5
void Func(vetor, tamanho)
int *vetor; //Esta declaração é equivalente a int vetor[];  
int tamanho;
{
  int i;
  for (i=0; i < tamanho; i++)
     vetor[i]=99;
}
void main ()
{
  int i;
  int v[TAM]={1, 2, 3, 4, 5};
  for (i=0; i < TAM; i++)
     printf("Vetor1[%d]= %d\n", i, v[i]);
  Func(v, TAM);
  for (i=0; i < TAM; i++)
     printf("Vetor2[%d]= %d\n", i, v[i]);
}
Note que a declaração formal do parâmetro vetor pode ser declarada de duas formas:
int *vetor; 
ou
int vetor[]; 
as duas são equivalentes, mas a primeira é preferível por explicitar que a variável é um ponteiro.

Ponteiros e strings

Como os strings são vetores de caracteres, cujos caracteres podem ser acessados por índices, o uso de ponteiros com strings é equivalente ao uso de ponteiros com vetores.

Função Strlen com ponteiros

 #include <stdio.h>
 int Strlen(char *s);
 void main ()
 {
   char c[50]="Brasil";
   int len;
   len = Strlen(c);     /*Argumento é um ponteiro para a string c*/
   printf("Comprimento da string: %d\n", len);
 }
 int Strlen(char *s)
 {
   int i;
   for (i=0; *s != '\0'; s++)
      i++;
   return (i);
 }
Note que o ponteiro para a string c (equivalente a &c[0]) é passado como argumento para a função Strlen, a qual é copiada para o parâmetro *s. Portanto, internamente a função é possível incrementar a variável ponteiro s com s++.
Compare com a função Strlen sem ponteiros vista no estudo de Strings.

Função Strcat com poteiros

Recebe duas strings como parâmetros e concatena as strings na primeira.

#include <stdio.h>
void Strcat(char *s1, char *s2);
void main ()
{
  char string1[20]="Brasil", string2[20]="2014";
  Strcat(string1, string2); /*Os argumentos são ponteiros para as strings*/
  printf("Strings concatenadas: %s\n", string1);
}
void Strcat(char *s1, char *s2)
{
  for (; *s1 != '\0'; s1++)
     ;
  for (; *s2 != '\0'; s1++, s2++)
     *s1=*s2;
}
Detalhe do funcionamento da função:
  • O segundo for poderia ser escrito da forma:
 for (; *s2 != '\0'; *s1++=*s2++)
    ;
neste caso, o valor apontado por s1 é primeiro igualado ao apontado por s2 e depois os ponteiros s1 e s2 são incrementados.
Compare com a função Strcat sem ponteiros vista no estudo de Strings.

Veja referência interessante sobre ponteiros[3].

Referências

  1. 1,0 1,1 KERNIGHAN, B.W.; RITCHIE, D.M. The C Programming Language, Prentice Hall, 2o ed. 1978.
  2. 2,0 2,1 2,2 TENENBAUM, Aaron M.; LANGSAM, Yedidyah; AUGENSTEIN, Moshe. Estruturas de dados usando C. Makron Books, 1995.
  3. http://homepages.dcc.ufmg.br/~joaoreis/Site%20de%20tutoriais/c_int/ponteiros.htm

--Evandro.cantu (discussão) 12h44min de 29 de agosto de 2014 (BRT)