Mudanças entre as edições de "PR1022804 2023 2 AULA11"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(17 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
=Operações com Arquivos=
+
=Ponteiros=
  
 
;OBJETIVOS
 
;OBJETIVOS
  
*O sistema de arquivos no Linux;
+
*Introdução ao uso de ponteiros no C;
*Tratamento de arquivos no C;
+
*Vetor de ponteiros;
*O acesso com funções de alto nível.
+
*Argc e argv;
 +
*Ponteiros para qualquer coisa;
 +
*Ponteiros para estruturas.
  
O sistema de arquivos é um "conjunto de estruturas lógicas" usado em todos os HDs, SSDs e chips de memória flash. Caso os componentes não tenham o sistema, os dados armazenados não poderão ser localizados e muito menos lidos em computadores e celulares com Windows, Linux, iOS ou Android. Na prática, um sistema de arquivo (''file system'') é um conjunto de estruturas lógicas, ou seja, feitas diretamente via software, que permite ao sistema operacional ter acesso e controlar os dados gravados no disco.
 
  
Cada sistema operacional lida com um sistema de arquivos diferente e cada sistema de arquivos possui as suas peculiaridades, como limitações, qualidade, velocidade, gerenciamento de espaço, entre outras características. É o sistema de arquivos que define como os bytes que compõem um arquivo serão armazenados no disco e de que forma o sistema operacional terá acesso aos dados. Para efeito dos nossos estudos vamos utilizar o sistema de arquivos do Linux.
+
;PARA PENSAR
  
 +
*A memória de um computador pode ser vista como um vetor de bytes.
 +
*Cada byte possui um endereço.
 +
*O tamanho da memória é definido pelo tamanho do barramento de endereços usado para acessá-la.
 +
*Uma variável ocupa uma área da memória.
 +
*Tipicamente uma variável to tipo ''char'' se utiliza de um ''byte''.
 +
*Já uma variável do tipo ''int'' pode (dependendo do sistema) usar 4 ''bytes'' contíguos.
  
==O Arquivo==
+
  
Um arquivo é um conjunto de dados que pode ser referenciado por um nome e pode ter outros atributos tais como: permissão para leitura e escrita, data da criação, data da última modificação etc.
+
;Uma variável possui um endereço e um conteúdo (dados).
 +
*Uma variável ponteiro tem como conteúdo um endereço.
 +
*Portanto a variável ponteiro também possui um endereço e contém um endereço como conteúdo.
  
Note que um arquivo pode conter dados como por exemplo: um programa C, um programa executável, textos, música, vídeos e fotos. Seja qual for a natureza dos dados o armazenamento será na forma de bits.
+
<blockquote style="background:#E6E6FA; border: 2px solid #8A2BE2; margin-left: 200px; margin-right: 200px; padding: 2em;">
  
Normalmente, arquivos são armazenados em memórias secundárias, tais como CD e HD (''Hard Disk''). Também podem ser armazenados na memória principal RAM ou uma memória rápida FLASH.
+
;ATENÇÃO:<span style="color:red;">'''Seu professor adverte: O uso descuidado de ponteiros pode causar dor de cabeça!</span>
  
Quanto a forma como os dados são armazenados, podemos dizer que os arquivos são [http://en.wikipedia.org/wiki/Binary_file binários] ou [http://en.wikipedia.org/wiki/Text_file texto]. Qualquer um deles armazena bits em que em conjunto de 8, representam os bytes, que por sua vez estão associados a um código ASCII.
+
</blockquote>
 +
==Ponteiro para inteiro==
  
==Sistema de Arquivos==
+
Observe o programa abaixo. A variável ''p'' é um ponteiro para inteiro. Isto significa que ela pode armazenar um endereço de um inteiro.
  
Um sistema tal como o Linux possui milhares de arquivos. Para que arquivos possam ser acessados e armazenados de forma consistente, eles são organizados em um sistema de arquivos.
+
<syntaxhighlight lang=c>
 +
#include <stdio.h>
  
Tipicamente, um sistema de arquivos ocupa uma área de um disco (ou mídia de armazenamento).
+
void main()
Nesta área ficam armazenados blocos de armazenamento dos dados dos arquivos e também as estruturas chamadas de '''inodes'''.
+
{
 +
  int x;
 +
  int *p;
  
Um [http://upload.wikimedia.org/wikipedia/commons/a/a2/Ext2-inode.gif inode] é um estrutura que possui as propriedades do arquivo e ponteiros para os blocos que contém os dados do arquivo. Tipicamente um sistema de arquivos possui uma lista de inodes que permite "indexar" cada um dos arquivos do sistema de arquivos.
+
  x=5;
 +
  printf("Valor de x antes = %d\n", x);
 +
 
 +
  p = &x;
 +
  *p=10;
  
Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc. No Windows os mais conhecidos são FAT32 e NTFS.
+
  printf("Valor de x depois = %d\n", x);
 +
  printf("Valor de p = %p\n", p);
 +
}
 +
</syntaxhighlight>
  
Um sistema de arquivos normalmente possui uma estrutura de dados inicial chamada de superbloco. O superbloco traz informações sobre o tamanho de blocos do sistema, o início da lista de inodes (/).
+
Observe que para se referenciar o conteúdo da posição de memória apontada por ''p'' deve-se usar o asterisco: ''*p''.
  
  
;SISTEMA DE ARQUIVOS
+
;EXEMPLO 1: Considere o programa abaixo:
 +
<syntaxhighlight lang=c>
 +
void main()
 +
{
 +
  int x=10;
 +
  int y, *p;
  
{| border="1" cellpadding="10" cellspacing="10"
+
}
! style="background:#8A2BE2; color:white;" | superbloco
+
</syntaxhighlight>
! style="background:#8A2BE2; color:white;" | lista de ''i''-nodes
+
Complete o código para copiar o conteúdo de x para y, sem que qualquer variável apareçam no lado esquerdo de um sinal de atribuição. Ou seja, sem envolver diretamente x e y.
! style="background:#8A2BE2; color:white;" | blocos dos arquivos
+
 
|}
+
;EXEMPLO 2: Tente inferir qual seria o valor da variável y no final do programa abaixo:
<br>
+
<syntaxhighlight lang=c>
 +
void main()
 +
{
 +
  int x,y,w,*p1,*p2;
 +
  x = 20;
 +
  w = 30;
 +
  p1 = &x;
 +
  p2 = &w;
 +
  y = *p1 + *p2;
 +
}
 +
</syntaxhighlight>
 +
 
 +
;EXEMPLO 3: Tente inferir qual seria o valor da variável y no final do programa abaixo:
 +
<syntaxhighlight lang=c>
 +
void main()
 +
{
 +
  int x,y,w,*p1,*p2, *p3;
 +
  x = 20;
 +
  w = 30;
 +
  p1 = &x;
 +
  p2 = &w;
 +
  y = *p1 + w;
 +
  p3 = &y;
 +
  *p3 = *p3 + 10;
 +
  y = *p1 + *p2 + *p3;
 +
}
 +
</syntaxhighlight>
  
==Diretórios==
+
;EXEMPLO 4: Qual seria o valor das variáveis '''y''' e '''x''' no final do programa abaixo:
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
  
São arquivos especiais que contém basicamente uma lista contendo nome e inode correspondente dos arquivos que o diretório contém.
+
void main()
 +
{
 +
int x,y;
 +
int *p;
 +
y=0;
 +
p=&y;
 +
x=*p;
 +
x=4;
 +
(*p)++;
 +
x--;
 +
(*p) += x;
 +
printf("\ny=%d x=%d\n",y,x);
 +
}
 +
</syntaxhighlight>
  
Em um sistema de arquivos, o diretório / é o diretório raiz do sistema, a partir do qual pode-se encontrar todos os demais arquivos.
+
==Ponteiro para char==
  
==Referência a um arquivo==
+
Os ponteiro para ''char'' são muito utilizados pois permitem apontar para ''strings''. A ideia é que ele aponte para o primeiro caracter (char) da ''string''. Veja o exemplo abaixo.
  
A localização de um arquivo pode ser realizada pela referência absoluta, ou seja, desde o diretório / do sistema:
+
<syntaxhighlight lang=c>
 +
#include <stdio.h>
  
cat /etc/passwd
+
void main()
 +
{
 +
  char x[10]="ifsc";
 +
  char *p;
  
O comando cat tem por objetivo mostrar no terminal o conteúdo do arquivo ''passwd''. Para que este arquivo seja encontrado na árvore de diretórios, deve-se fornecer a referência absoluta, desde o diretório /
+
  p = &x[2];
  
Uma alternativa de acesso, é o uso da referência relativa ao diretório de trabalho. O conceito de diretório de trabalho é criado pelo interpretador de comandos (''shell''). Desta forma pode-se por exemplo fazer o comando:
+
  printf("x[2] = %c\n", *p);
  
cat passwd
+
  p = x;
  
O sistema procurará o arquivo passwd dentro do diretório de trabalho, que é referenciado armazenado pelo ''shell''. Neste caso, o diretório de trabalho deveria ser ''/etc''
+
  printf("string %s\n", p);
  
cd /etc
+
  while (*p!=0) {
cat passwd
+
      printf("Endereco %p conteúdo %c\n", p,*p);
 +
      p++;
 +
  }
 +
}
 +
</syntaxhighlight>
  
==No Linux/Unix tudo é arquivo==
+
Neste foi usado o incremento de um ponteiro, o que implica em adicionar ao endereço armazenado em ''p'' uma quantidade relativa ao tamanho do tipo apontado.
 +
No caso é 1 (tamanho de um ''char'' é um byte).
  
No Linux, qualquer referência/acesso ao hardware/dispositivos é realizada na forma de acesso a arquivo. Ver arquivos no diretório de dispositivos:
+
;EXEMPLO: Sem executar o programa abaixo, determine o valor de y no final do programa:
 +
<syntaxhighlight lang=c>
 +
void main()
 +
{
 +
  char x[10]="ifsc";
 +
  char *p, y;
 +
 
 +
  p = x + 2;
 +
  y= *p;
 +
}
 +
</syntaxhighlight>
  
  ls -l /dev
+
==Apontando para um vetor de inteiros==
  
Como consequência, a partir de um programa em C, é possível abrir e escrever/ler em um dispositivo (desde que se tenha autorização).
+
Da mesma forma que usamos um ponteiro para ''char'' para apontar uma ''string'', podemos fazer um ponteiro para ''int'' apontar para para um elemento de um vetor de inteiros.
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
  
=Acessando arquivos a partir de programas C=
+
void main()
 +
{
 +
  int x[10]= {0,1,2,3,4,5,6,7,8,9};
 +
  int *p;
 +
  int i;
 +
 
 +
  p = x;
  
==Acesso a arquivos: funções de baixo e alto nível==
+
  i=0;
 +
  while (i<10) {
 +
      printf(" endereco %p e conteudo %d\n", p, *p);
 +
      p++;
 +
      i++;     
 +
  }
 +
}
 +
</syntaxhighlight>
  
Em sistemas do porte do Linux e Windows, por questões de segurança e controle, todo acesso a arquivo é realizado através de código do sistema operacional. Desta forma, um programa que deseja acessar um arquivo deve gerar uma CHAMADA AO SISTEMA.
+
;OBSERVE: Que p++ incrementa em 4 unidades.
O Linux, assim como outros sistemas operacionais, possui uma API (Application Programming Interface) bem definida, na forma de um conjunto de chamadas que permitem realizar uma série de funcionalidades (serviços) para os processos (programas em execução).
 
  
Um programa em C realiza uma CHAMADA AO SISTEMA com auxílio de funções da biblioteca C (a glib no caso do Linux).
+
==Usando ponteiro na passagem de parâmetros==
Duas categorias de funções para acesso a arquivo são disponibilizadas [http://en.wikipedia.org/wiki/C_file_input/output (ver detalhes aqui)]:
 
*Funções de baixo nível: acessam diretamente o sistema;
 
*Funções de alto nível: as funções intermediam o acesso, criando buffers no espaço de endereçamento do processo. As funções de baixo nível são invocadas para ler e escrever dados no buffer.
 
  
==Acesso a arquivo em MODO TEXTO através de funções de alto nível==
+
Observe como podemos usar ponteiros na passagem de parâmetros.
  
O acesso em alto nível é realizado usando uma estrutura do tipo [http://www.gnu.org/software/libc/manual/html_node/Streams.html#Streams FILE] definida no stdio.h.
+
<syntaxhighlight lang=c>
Todo acesso passa inicialmente por abrir o arquivo (função fopen), ler/escrever (várias funções, tipo fread(), fwrite()) e fechar o arquivo (fclose()).
+
#include <stdio.h>
  
A tabela abaixo apresenta as principais funções da
+
void str_cpy(char *pdest, char *pfonte)
linguagem C para manipulação de arquivos:
+
{
 +
  while (*pfonte!=0) {
 +
        *pdest++ = *pfonte++;
 +
  }
 +
  *pdest = 0;
 +
}
  
{| border="1" cellpadding="5" cellspacing="0"
 
! style="background:#8A2BE2; color:white;;" | Função
 
! style="background:#8A2BE2; color:white;;" | Ação
 
|-
 
!style="text-align: center;" | fopen()
 
|Abre um arquivo.
 
|-
 
!style="text-align: center;" | fclose()
 
|Fecha um arquivo.
 
|-
 
!style="text-align: center;" | putc() e fputc()
 
|Escreve um caractere em um arquivo
 
|-
 
!style="text-align: center;" | getc() e fgetc()
 
|Lê um caractere de um arquivo.
 
|-
 
!style="text-align: center;" | fseek()
 
|Posiciona em um registro de um arquivo
 
|-
 
!style="text-align: center;" | fprintf()
 
|Efetua impressão formatada em um arquivo.
 
|-
 
!style="text-align: center;" | fscanf()
 
|Efetua leitura formatada em um arquivo.
 
|-
 
!style="text-align: center;" | feof()
 
|Verifica o final de um arquivo.
 
|-
 
!style="text-align: center;" | fwrite()
 
|Escreve tipos maiores que 1 byte em um arquivo.
 
|-
 
!style="text-align: center;" | fread()
 
|Lê tipos maiores que 1 byte de um arquivo.
 
|}
 
<br>
 
  
==Aprendendo com Exemplos==
+
int str_len (char *p)
 +
{
 +
  int i=0;
 +
  while (*p++!=0)
 +
i++;
 +
  return i;
 +
}
  
Nos exemplos que se seguem, serão usadas as funções [http://www.cplusplus.com/reference/cstdio/fopen/ fopen] e [http://www.cplusplus.com/reference/cstdio/fclose/?kw=fclose fclose], para abrir e fechar arquivo e [http://www.cplusplus.com/reference/cstdio/fprintf/?kw=fprintf fprintf()] e [http://www.cplusplus.com/reference/cstdio/fscanf/?kw=fscanf fscanf()] para leitura e escrita.
+
void main()
A forma é similar ao ''printf'' e ''scanf''(ver http://diuf.unifr.ch/pai/ip/tutorials/c/TC02_scanf.pdf), a não ser pelo fato de que a escrita e leitura é realizada no arquivo indicado
+
{
por  ''p_arq''.
+
  char fonte[10]="ifsc";
 +
  char destino[10];
  
<blockquote style="background:#E6E6FA; border: 2px solid #8A2BE2; margin-left: 100px; margin-right: 100px; padding: 2em;">
+
  str_cpy(destino, fonte);
;Modos de abertura de arquivos – fopen():
+
  printf("string destino = %s\n", destino);
:        w -> Escrita
 
:        r -> leitura
 
:        a -> anexar
 
:        r+ -> leitura e escrita
 
:        w+ -> leitura e escrita (apaga o conteúdo caso o arquivo exista)
 
:        a+ -> leitura e escrita (adiciona ao final do arquivo)
 
</blockquote>
 
  
;EXEMPLO 1:
+
  printf("tamanho de dest = %d\n", str_len(destino));
 +
}
 +
</syntaxhighlight>
  
*Escrevendo um arquivo texto de forma formatada:
+
Um ponto interessante é que ponteiros permitem, na chamada de uma função, passar valores por referência:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
#include <stdio.h>
+
 
 +
void alfa(int *p)
 +
{
 +
  *p=10;
 +
}
  
 
void main()
 
void main()
 
{
 
{
   FILE *p_arq;
+
   int x;
   int i;
+
  x =5;
   int res;
+
  printf("Valor de x antes da chamada de alfa = %d\n", x);
 +
   alfa(&x);
 +
   printf("Valor de x depois da chamada de alfa = %d\n", x);
 +
}
 +
</syntaxhighlight>
 +
 
 +
==Ponteiros para ponteiros==
 +
Um ponteiro para um ponteiro é como se você anotasse o endereço de um ponteiro na agenda que tem o endereço da casa do seu amigo. Pode-se declarar um ponteiro para ponteiro com a seguinte notação:
 +
 
  
  if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
+
:;tipo_da_variavel **nome_da_variavel_ponteiro;
    printf("Problemas na abertura do arquivo\n");
 
    return;
 
  }
 
  
  for (i = 0; i<10;i++) {
 
  /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
 
      if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {       
 
        printf("Erro\n");
 
  break;
 
      }
 
  }
 
  fclose(p_arq);
 
}
 
</syntaxhighlight>
 
  
Note que se o arquivo IFSC.txt não existir, ele será criado.
+
Sendo que o '''**nome_da_variavel_ponteiro''' é o conteúdo final da variável apontada, e '''*nome_da_variavel_ponteiro''' é o conteúdo do ponteiro intermediário.
No linux para ver o que foi escrito faça:
 
> cat IFSC.txt
 
  
*Lendo um arquivo texto de forma formatada:
+
;NOTE: Na linguagem C pode-se declarar ponteiros para ponteiros para ponteiros para ponteiros... e assim por diante.
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
+
 
 
void main()
 
void main()
 
{
 
{
  FILE *p_arq;
+
float fpi=3.1415, *pf, **ppf;
  int i,j;
+
pf=&fpi; // pf armazena o endereco de fpi
  int res;
+
ppf=&pf; // ppf armazena o endereco de pf
  char buff[100];
+
printf("\n%f", **ppf); // imprime o valor de fpi por ppf
+
printf("\n%f", *pf); // imprime o valor de fpi por pf
  if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
 
    printf("Problemas na abertura do arquivo\n");
 
    return;
 
  }
 
 
  while(1) {
 
      if((res = fscanf(p_arq,"%s %d",buff,&j))==EOF) {       
 
        printf("Fim de leitura\n");
 
  break;
 
      }
 
      printf("%s %d\n",buff,j);
 
  }
 
  fclose(p_arq);
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caracter EOF (''end-of-file'') quando não existe mais dados a serem lidos.
+
;NOTE: Para acessar o valor apontado por um ponteiro para ponteiro, o operador asterisco deve ser aplicado duas vezes.
  
;EXERCÍCIO: Implementar uma função que some duas matrizes fornecidas em arquivos de texto separados: MatA.dat e MatB.dat. Colocando o resultado em um outro arquivo de texto com o nome MatR.dat.
+
=Vetor de ponteiros=
  
> cat MatA.dat
+
Como visto em aulas anteriores, variáveis ponteiros possuem como conteúdo um endereço.
 +
É perfeitamente possível construir vetores e matrizes de ponteiros.  
  
<pre>
+
;EXEMPLO: Veja o programa abaixo:
1
 
2
 
3
 
4
 
5
 
5
 
4
 
3
 
2
 
1
 
1
 
2
 
3
 
4
 
5
 
5
 
4
 
3
 
2
 
1
 
1
 
2
 
3
 
4
 
5
 
</pre>
 
''* esse é o arquivo MatA.dat com 25 elementos (matriz 5x5)''
 
  
> cat MatB.dat
 
<pre>
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
2
 
</pre>
 
''* esse é o arquivo MatB.c com 25 elementos (matriz 5x5)''
 
 
{{collapse top|bg=#E6E6FA|Solução}}
 
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
+
 
void main()
+
int main()
 
{
 
{
  FILE *p_arq;
+
   int i;
   int i,j,res;
 
  float MatA[5][5], MatB[5][5], MatR[5][5];
 
 
 
  /* Ler a matriz MatA do arquivo */
 
  if ((p_arq=fopen("MatA.dat", "r")) == NULL) {
 
    printf("Problemas na abertura do arquivo\n");
 
    return;
 
  }
 
 
 
  for (i =0;i<5;i++) {
 
      for (j=0;j<5;j++) {
 
      if((res = fscanf(p_arq,"%f",&MatA[i][j]))==EOF){     
 
        printf("Fim de leitura\n");
 
break;
 
      }
 
      printf("%f ",MatA[i][j]);
 
      }
 
      printf("\n");
 
  }
 
  fclose(p_arq);
 
 
 
  /* Ler a matriz MatB do arquivo */
 
  
   printf("\n\n");
+
   char *vp[4];
     
+
   char alfa[5]="IFSC";
   /* Ler a matriz MatB do arquivo */
+
   char beta[5]="TELE";
  if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
+
  char delta[5]="RAC";
    printf("Problemas na abertura do arquivo\n");
+
  char gamma[5]="CGER";
    return;
 
   }
 
 
 
  for (i =0;i<5;i++) {
 
      for (j=0;j<5;j++) {
 
      if((res = fscanf(p_arq,"%f",&MatB[i][j]))==EOF){     
 
        printf("Fim de leitura\n");
 
break;
 
      }
 
      printf("%f ",MatB[i][j]);
 
      }
 
      printf("\n");
 
  }
 
  fclose(p_arq);
 
   
 
  /* Calcular a soma das matrizes e colocar resultado em MatR */
 
  
   for (i =0;i<5;i++) {
+
   vp[0] = alfa;
      for (j=0;j<5;j++) {
+
  vp[1] = beta;
      MatR[i][j]=MatA[i][j]+MatB[i][j];
+
   vp[2] = delta;
      }
+
   vp[3] = gamma;   
   }
 
  printf("Resultado da Adição\n");  
 
   for (i =0;i<5;i++) {
 
      for (j=0;j<5;j++) {
 
          printf("%f ", MatR[i][j]);   
 
      }
 
      printf("\n");     
 
  } 
 
  /* Armazenar MatR em arquivo */
 
  
  if ((p_arq=fopen("MatR.dat", "w")) == NULL) {
+
   for(i=0;i<4;i++)
    printf("Problemas na abertura do arquivo\n");
+
printf("%s\n", vp[i]);
    return;
 
  }
 
 
 
   for (i =0;i<5;i++) {
 
      for (j=0;j<5;j++) {
 
        if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {     
 
        printf("erro\n");
 
break;
 
      }
 
      }
 
      fprintf(p_arq,"\n");
 
  }
 
  fclose(p_arq);      
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
{{collapse bottom}}
 
  
;EXEMPLO 2: Neste exemplo usaremos as funções '''fgetc''' e '''fputc''' para ler e escrever caracteres ASCII nos arquivos. Seja um programa que conta o número de ocorrências do caractere 'a' em um arquivo e reescreve o arquivo sem os "as".
+
[[imagem:Fig1Aula24PrgITele.jpg|700px]]
  
Exemplo do arquivo ''teste.dat''*:
+
Observe que vp é um vetor de ponteiros para char e cada elemento aponta para uma cadeia de caracteres.
  
> cat teste.dat
+
==Argumentos de linha de comando==
<pre>
+
 
Pangrama, ou pantograma, (do grego, pan ou pantós = todos, + grama = letra) é uma frase em que são usadas todas as letras do alfabeto de determinada língua.
+
Um bom exemplo de vetor de ponteiros é a passagem de parâmetros na linha de comando. Cada parâmetro é tratado como uma cadeia de caracteres apontada por um elemento do vetor ''argv''. O número de parâmetros é passado em ''argc''. '''Note que argv[0]''' aponta para uma ''string ''que indica o nome do programa.
</pre>
+
 
''* conteúdo do arquivo teste.dat''
+
;EXEMPLO: Teste o programa abaixo:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
#include <stdlib.h>
+
 
void main()
+
int main(int argc, char *argv[])
 
{
 
{
  FILE *fp_fonte,*fp_destino;
+
  int i;
  int x,cont=0;
 
                 
 
  if((fp_fonte=fopen("teste.dat","r")) == NULL) {
 
puts("Não conseguiu abrir o arquivo\n");
 
exit(1);
 
  }
 
  if((fp_destino=fopen("dest.dat","w")) == NULL){
 
puts("Não conseguiu abrir o arquivo\n");
 
exit(1);
 
  }
 
  
  /* note que fgetc retorna um inteiro contendo o ASCII lido */
+
  for (i=0;i<argc;i++) {
  while ((x=fgetc(fp_fonte)) != EOF){
+
      printf("%s\n", argv[i]);
      if(x=='a')
+
  }
        cont++;
+
  printf("Numero de parametros passados = %d\n", argc-1); /* o primeiro é o nome do arquivo executavél" */
      else
 
if((x=fputc(x,fp_destino))==EOF) {
 
puts("Não conseguiu abrir o arquivo\n");
 
exit(1);
 
}
 
  }
 
  printf("ocorrências de a = %d\n", cont);
 
  fclose(fp_fonte);
 
  fclose(fp_destino);
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
No linux, para ver o arquivo ''dest.dat'' faça:
+
==Apontando para estruturas==
 
 
> cat dest.dat
 
 
 
<blockquote style="background:#E6E6FA; border: 2px solid #8A2BE2; margin-left: 100px; margin-right: 100px; padding: 2em;">
 
;NOTE
 
*Que '''fputc''' recebe o caracter como um inteiro mas realiza um ''cast'' para ''unsigned char'' com fins de escrevê-lo no arquivo.
 
*Substitua 'a' por '\n' e veja o que acontece com o arquivo destino.
 
</blockquote>
 
  
;EXEMPLO 3: Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um '''append''' (escrever no final do arquivo).
+
Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
#include <time.h>
 
 
#include <stdio.h>
 
#include <stdio.h>
 
int main(void)
 
{
 
  time_t ltime;
 
  FILE *fp;
 
  int num;
 
  
   time(&ltime);  
+
struct TRegistro {
 +
   char nome[20];
 +
  int idade;
 +
} Tabela[4] = {
 +
          {"joao",18,},
 +
          {"maria",18,},
 +
          {"jose",19,},
 +
          {"lara",17,},
 +
}
 +
;
 +
 
 +
struct TRegistro *p;
  
  if ((fp=fopen("DATA.txt", "a")) == NULL) {
+
void main()
      printf("Problemas na abertura do arquivo\n");
+
{
      return;
+
  p = &Tabela[3]; /*p aponta para o registro 3 da tabela */
  }
+
  printf("O nome na posição 3 é %s e idade = %d\n", p->nome,p->idade);
  if ((num = fputs( ctime(&ltime), fp )) != EOF ) {
 
      fclose(fp);
 
  } else {
 
      printf("Erro na escrita do arquivo!\n");
 
  }
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Execute o programa da forma (suponha que se chame ''log_tempo.c''):
+
;NOTE: Que o uso de '''p->nome''' é uma alternativa ao uso de (*p).nome
> ./log_tempo
 
> cat DATA.txt
 
> ./log_tempo
 
> cat DATA.txt
 
> ./log_tempo
 
> cat DATA.txt 
 
 
 
;EXERCÍCIO: Substitua o modo de '''append''' por uma simples escrita (w). Reexecute o programa conforme especificado anteriormente.
 
 
 
==Ainda funções de acesso a arquivos==
 
  
A localização corrente do acesso a um arquivo pode ser modificada antes de uma leitura ou escrita. Tipicamente, quando se abre
+
No primeiro caso pode-se ler: o campo nome do objeto que é apontado por p.
uma arquivo para leitura/escrita, o "cursor" de acesso é 0, ou seja início do arquivo. Se o arquivo for aberto em modo append, o "cursor" é posicionado no final. A cada acesso (leitura ou esrita), este cursor é incrementado conforme o número de dados lidos ou escritos.
 
  
É possível entretanto modificar a posição deste cursor, tipicamente com as funções ''fseek()'' e ''rewind()''. (ver http://www.gnu.org/software/libc/manual/html_node/File-Positioning.html#File-Positioning)
+
==Retornando uma estrutura em uma função==
  
Considere o programa abaixo que escreve um vetor de 100 inteiros em um arquivo.
+
No exemplo a abaixo a função ''RetornarStruct()'' retorna um ponteiro para uma estrutura.
 +
O cuidadado que se deve ter é que a função não deveria apontar para uma estrutura que foi criada localmente
 +
na função!
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
 +
struct TRegistro {
 +
  char nome[20];
 +
  int idade;
 +
} Tabela[4] = {
 +
          {"joao",18,},
 +
          {"maria",18,},
 +
          {"jose",19,},
 +
          {"lara",17,},
 +
};
  
int x[100];
+
struct TRegistro *p;
 +
 
 +
struct TRegistro * RetornarStruct(int indice)
 +
{
 +
  return &Tabela[indice];
 +
}
  
 
void main()
 
void main()
 
{
 
{
   FILE *fp;
+
   p = RetornarStruct(2); /*p aponta para o registro 3 da tabela */
 
+
   printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
  fp = fopen("teste.dat","w");
 
   x[0]=32;
 
  x[90]=11;
 
  fwrite(x, sizeof(int),100,fp);
 
  fclose(fp);
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
;Note
+
==Passando uma estrutura como parâmetro==
#Que o vetor, sendo uma variável global não inicialiazada, será zerado pelo procedimento de ''startup'' do programa. Entretanto, a posição 90 recebe um valor (11).
 
#Não é possível visualizar o conteúdo do arquivo porque a função '''fwrite()''' grava o arquivo em formato binário.
 
  
{{collapse top|Ler o arquivo gravado fwrite()}}
 
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
 +
struct TRegistro {
 +
  char nome[20];
 +
  int idade;
 +
} Tabela[4] = {
 +
          {"joao",18,},
 +
          {"maria",18,},
 +
          {"jose",19,},
 +
          {"lara",17,},
 +
};
  
int x[100];
+
struct TRegistro *p;
 +
 
 +
void MudarStruct(struct TRegistro *p1, int indice)
 +
{
 +
  Tabela[indice] = *p1;
 +
}
  
 
void main()
 
void main()
 
{
 
{
   FILE *fp;
+
   struct TRegistro aux = {"luisa",16};
  
   fp = fopen("teste.dat","r");
+
   MudarStruct(&aux,2);
   fread(x, sizeof(int),100,fp);
+
   p = &Tabela[2];
   fclose(fp);
+
   printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
  for(int i=0;i<100;i++)
 
    printf("%d\n",x[i]);
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
{{collapse bottom}}
 
  
 +
Exercício: No programa acima construir uma função que imprime a Tabela  usando ponteiros. A função deve receber como parâmetro um ponteiro para o início da tabela e o tamanho da tabela.
 +
 +
=Exercícios=
 +
 +
1. Implementar a função ''str_cat'' que concatena duas ''strings'' usando ponteiros.
 +
 +
2. Ordenar valores de um vetor de inteiros passando por referencia o ponteiro para esse vetor.
 +
 +
3. Implememtar um programa que recebe 3 parâmetros na linha de comando: dois números reais e um operador (char). Operador pode ser + ou menos. O programa deve mostrar o resultado da operação.
 +
:Exemplo:  calcula 3.5 + 2.6
 +
:para usar a função '''atof''' para converter '''string''' em '''float'''.
  
Agora observe o programa seguinte que lê especificamente a posição 90 e na sequência restabelece o cursor no início. Note que o '''fread()''' lê somente um inteiro na posição corrente do arquivo. A posição corrente foi determinada por ''fseek'' e depois retornada para o início com ''rewind''.
+
4.Implementar um programa chamado ''cmpcadeia'' que testa se duas strings passadas na linha de comando são iguais. O programa deve imprimir uma mensagem indicando se são iguais ou diferentes. Usar a função strcmp da biblioteca. Caso sejam passados mais ou menos que dois parâmetros o programa deve se encerrar mostrando uma indicão do tipo:
 +
:cmpcadeia: dois parametros devem ser passados.
  
 +
5. Renomeie o executável e veja seja a mensagem de erro mostra o nome correto do programa.
  
 +
6. Implementar um programa para ler dados para dentro das variáveis x e y e somar o conteúdo das mesmas colocando o resultado em x SEM
 +
referenciar estas variáveis no scanf ou na expressão de soma.
 +
2. Implementar uma função que compara duas strings passadas como parâmetro. A função retorna 0 se as strings forem iguais e 1 se diferentes.
 +
Usar ponteiros.
 +
7. Implementar uma função que recebe como parâmetro o endereço de duas variáveis float que contêm a parte real e imaginária de um número complexo no formato polar (ângulo em radianos). A função deve converter do formato polar retangular colocando a coordenada x no primeira variável cujo endereço foi fornecido como parâmetro e a coordenada y na segunda variável (Olhar AULA 15).
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
#include <stdio.h>
 
  
int y;
+
void converte_polar_retang(float *parte1, float *parte2)
 +
{
 +
}
  
 
void main()
 
void main()
 
{
 
{
   FILE *fp;
+
   float num1=1.5, num2=10.6;
  
   fp = fopen("teste.dat","r");
+
   /*chamar a função aqui */
  fseek(fp, 90*sizeof(int), SEEK_SET);
+
 
  fread(&y, sizeof(int),1,fp);
+
   /* imprimir os valores de num1 e num2 aqui */
   printf("y=%d\n", y);
 
  rewind(fp);
 
  fread(&y, sizeof(int),1,fp);
 
  printf("y=%d\n", y);
 
  fclose(fp);
 
 
}
 
}
</syntaxhighlight>
+
</syntaxhighlight>  
 +
8. Implemantar uma função que recebe como parâmetro o endereço de duas variáveis do tipo char e
 +
após a chamada da função os valores das variáveis devem ser convertidos para caixa alta (maiúsculas).
 +
<syntaxhighlight lang=c>
 +
void main()
 +
{
 +
  char alfa='a', beta='b';
  
;Exercício: Considere o programa abaixo. Ele deve acessar uma tabela que se encontra em um arquivo binário. Cada item da tabela se apresenta conforme o registro TRegistro. Implemente a função ''LerTab'' e crie um programa para escrever uma tabela iniciada com uma tabela de 5 registros a fim de testar a função implementada.
+
  capitaliza(&alfa, &beta);
 +
 
 +
  /* aqui os valores de alfa e beta deverão ser A e B */
 +
}
  
<syntaxhighlight lang= c>
+
</syntaxhighlight>
#include <stdio.h>
+
9. Implementar uma função que recebe uma string contendo uma cadeia de caracteres com dígitos numéricos e retorna o valor inteiro da string. Usar ponteiros.
 
+
<syntaxhighlight lang=c>
struct TRegistro {
 
  char nome[30];
 
  int idade;
 
} Registro, *pAux;
 
  
struct TRegistro *LerTab(int indice)
+
int a_toi(char *p)
 
{
 
{
 
}
 
}
Linha 537: Linha 471:
 
void main()
 
void main()
 
{
 
{
   pAux=LerTab(3);
+
   char *p="123";
   if (pAux!=0) {
+
  int x;
      printf("Nome lido %s\n", pAux->nome);
+
 
   }
+
   x = a_toi(p);
 +
    
 +
  /* neste ponto x deve conter 123 */
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
10. O código abaixo implementa a série responsável pelo cálculo da raiz quadrada de um número.
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
float raiz_quadrada(float numero);
 +
 +
int main( int argc, char **argv)
 +
 +
    float numero;
 +
 
 +
    printf("Entre com um número positivo por favor : ");
 +
    scanf("%f",&numero);
 +
 
 +
    printf("A raiz quadrada de %.3f é %.5f \n",numero,raiz_quadrada(numero));
  
==Decompondo ''string'' com delimitadores==
+
    return(0);
 +
}
  
Esta é uma forma de resolver o problema com "espaço" no texto da ''string''. Pode ser utilizado conjuntamente com operações com arquivos.
 
  
 +
float raiz_quadrada (float numero)
 +
{
 +
    int n;
 +
    float recorre = numero;
 +
 
 +
    for (n = 0; n < 10; ++n)
 +
          recorre = recorre/2 + numero/(2*recorre);
 +
         
 +
    return(recorre);   
 +
}   
 +
</syntaxhighlight>
 +
11. O código abaixo implementa uma variação em que número é passado por parâmetro através do programa principal e retorna a raiz quadrada de um número para o SO.
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
#include <string.h>
 
 
#include <stdio.h>
 
#include <stdio.h>
 +
#include <stdlib.h>
 +
 +
float raiz_quadrada (float numero)
 +
{
 +
    int n;
 +
    float recorre = numero;
 +
 +
    for (n = 0; n < 10; ++n)
 +
          recorre = recorre/2 + numero/(2*recorre);
 +
 +
    return(recorre);   
 +
}
 +
 +
int main( int argc, char *argv[])
 +
 +
    float numero;
  
int main()
+
    if(argv[1]==NULL){
{
+
printf("\n%s: falta número",argv[0]);
  const char string_delimitada[80] = "alfa:beta:delta:epson";
+
    }
  const char s[2] = ":";
 
  char *token;
 
 
 
  /* Ler o primeiro campo passando a string em str e o delimitador em s */
 
  token = strtok(string_delimitada, s);
 
 
 
  /* Ler os pŕoximos campos - passar primeiro parâmetro NULL */
 
  while( token != NULL )  
 
  {
 
      printf( " %s\n", token );
 
 
      
 
      
      token = strtok(NULL, s);
+
    numero=atof(argv[1]);
  }
+
 
 
+
    printf("\n%.4f \n",raiz_quadrada(numero));
  return(0);
+
 +
    return(0);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=Material de Apoio=
+
=Referências=
  
[1] [https://drive.google.com/file/d/1HTDPuCYJYr7tcGbkO8Of2alDNSZiB6-A/view?usp=sharing Videoaula | Operações com Arquivos]
+
[1] http://pw1.netcom.com/~tjensen/ptr/ch1x.htm
  
 +
[2] http://eternallyconfuzzled.com/tuts/languages/jsw_tut_pointers.aspx
  
=Referências=
+
[3] http://duramecho.com/ComputerInformation/WhyCPointers.html
  
[1] https://www.techtudo.com.br/dicas-e-tutoriais/noticia/2016/02/entenda-o-que-e-sistema-de-arquivos-e-sua-utilidade-no-pc-e-no-celular.html
+
[4] http://boredzo.org/pointers/ http://boredzo.org/pointers/
  
[2] https://wagnergaspar.com/como-ler-um-arquivo-texto-com-a-funcao-fscanf/
+
[5] http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c600.html
  
[3] https://wagnergaspar.com/como-criar-uma-agenda-de-aniversario-e-salvar-em-arquivo-com-c/
+
[6] [https://drive.google.com/file/d/1xt3pF05rYewUaub4C1Gv_Aqp4kKB8Q4w/view?usp=sharing Videoaula | Ponteiros]
  
  

Edição atual tal como às 16h20min de 5 de outubro de 2023

Ponteiros

OBJETIVOS
  • Introdução ao uso de ponteiros no C;
  • Vetor de ponteiros;
  • Argc e argv;
  • Ponteiros para qualquer coisa;
  • Ponteiros para estruturas.


PARA PENSAR
  • A memória de um computador pode ser vista como um vetor de bytes.
  • Cada byte possui um endereço.
  • O tamanho da memória é definido pelo tamanho do barramento de endereços usado para acessá-la.
  • Uma variável ocupa uma área da memória.
  • Tipicamente uma variável to tipo char se utiliza de um byte.
  • Já uma variável do tipo int pode (dependendo do sistema) usar 4 bytes contíguos.


Uma variável possui um endereço e um conteúdo (dados).
  • Uma variável ponteiro tem como conteúdo um endereço.
  • Portanto a variável ponteiro também possui um endereço e contém um endereço como conteúdo.
ATENÇÃO
Seu professor adverte: O uso descuidado de ponteiros pode causar dor de cabeça!

Ponteiro para inteiro

Observe o programa abaixo. A variável p é um ponteiro para inteiro. Isto significa que ela pode armazenar um endereço de um inteiro.

#include <stdio.h>

void main()
{
  int x;
  int *p;

  x=5;
  printf("Valor de x antes = %d\n", x);
  
  p = &x;
  *p=10;

  printf("Valor de x depois = %d\n", x);
  printf("Valor de p = %p\n", p);
}

Observe que para se referenciar o conteúdo da posição de memória apontada por p deve-se usar o asterisco: *p.


EXEMPLO 1
Considere o programa abaixo:
void main()
{
  int x=10;
  int y, *p;

}

Complete o código para copiar o conteúdo de x para y, sem que qualquer variável apareçam no lado esquerdo de um sinal de atribuição. Ou seja, sem envolver diretamente x e y.

EXEMPLO 2
Tente inferir qual seria o valor da variável y no final do programa abaixo:
void main()
{
  int x,y,w,*p1,*p2;
  x = 20;
  w = 30;
  p1 = &x;
  p2 = &w;
  y = *p1 + *p2;
}
EXEMPLO 3
Tente inferir qual seria o valor da variável y no final do programa abaixo:
void main()
{
  int x,y,w,*p1,*p2, *p3;
  x = 20;
  w = 30;
  p1 = &x;
  p2 = &w;
  y = *p1 + w;
  p3 = &y;
  *p3 = *p3 + 10;
  y = *p1 + *p2 + *p3;
}
EXEMPLO 4
Qual seria o valor das variáveis y e x no final do programa abaixo:
#include <stdio.h>

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

Ponteiro para char

Os ponteiro para char são muito utilizados pois permitem apontar para strings. A ideia é que ele aponte para o primeiro caracter (char) da string. Veja o exemplo abaixo.

#include <stdio.h>

void main()
{
   char x[10]="ifsc";
   char *p;

   p = &x[2];

   printf("x[2] = %c\n", *p);

   p = x;

   printf("string %s\n", p);

   while (*p!=0) {
       printf("Endereco %p conteúdo %c\n", p,*p);
       p++;
   }	
}

Neste foi usado o incremento de um ponteiro, o que implica em adicionar ao endereço armazenado em p uma quantidade relativa ao tamanho do tipo apontado. No caso é 1 (tamanho de um char é um byte).

EXEMPLO
Sem executar o programa abaixo, determine o valor de y no final do programa:
void main()
{
   char x[10]="ifsc";
   char *p, y;
   
   p = x + 2;
   y= *p;
}

Apontando para um vetor de inteiros

Da mesma forma que usamos um ponteiro para char para apontar uma string, podemos fazer um ponteiro para int apontar para para um elemento de um vetor de inteiros.

#include <stdio.h>

void main()
{
   int x[10]= {0,1,2,3,4,5,6,7,8,9};
   int *p;
   int i;
   
   p = x;

   i=0;
   while (i<10) {
       printf(" endereco %p e conteudo %d\n", p, *p);
       p++;
       i++;       
   }	
}
OBSERVE
Que p++ incrementa em 4 unidades.

Usando ponteiro na passagem de parâmetros

Observe como podemos usar ponteiros na passagem de parâmetros.

#include <stdio.h>

void str_cpy(char *pdest, char *pfonte)
{
   while (*pfonte!=0) {
        *pdest++ = *pfonte++;
   }
   *pdest = 0;
}


int str_len (char *p)
{
   int i=0;
   while (*p++!=0)
	i++;
   return i;
}

void main()
{
   char fonte[10]="ifsc";
   char destino[10];

   str_cpy(destino, fonte);
   printf("string destino = %s\n", destino);

   printf("tamanho de dest = %d\n", str_len(destino));
}

Um ponto interessante é que ponteiros permitem, na chamada de uma função, passar valores por referência:

void alfa(int *p)
{
  *p=10;
}

void main()
{
  int x;
  x =5;
  printf("Valor de x antes da chamada de alfa = %d\n", x);
  alfa(&x);
  printf("Valor de x depois da chamada de alfa = %d\n", x);
}

Ponteiros para ponteiros

Um ponteiro para um ponteiro é como se você anotasse o endereço de um ponteiro na agenda que tem o endereço da casa do seu amigo. Pode-se declarar um ponteiro para ponteiro com a seguinte notação:


tipo_da_variavel **nome_da_variavel_ponteiro;


Sendo que o **nome_da_variavel_ponteiro é o conteúdo final da variável apontada, e *nome_da_variavel_ponteiro é o conteúdo do ponteiro intermediário.

NOTE
Na linguagem C pode-se declarar ponteiros para ponteiros para ponteiros para ponteiros... e assim por diante.
#include <stdio.h>

void main()
{
	float fpi=3.1415, *pf, **ppf;
	pf=&fpi;		// pf armazena o endereco de fpi
	ppf=&pf;		// ppf armazena o endereco de pf
	printf("\n%f", **ppf);	// imprime o valor de fpi por ppf
	printf("\n%f", *pf);	// imprime o valor de fpi por pf
}
NOTE
Para acessar o valor apontado por um ponteiro para ponteiro, o operador asterisco deve ser aplicado duas vezes.

Vetor de ponteiros

Como visto em aulas anteriores, variáveis ponteiros possuem como conteúdo um endereço. É perfeitamente possível construir vetores e matrizes de ponteiros.

EXEMPLO
Veja o programa abaixo:
#include <stdio.h>

int main()
{
  int i;

  char *vp[4];
  char alfa[5]="IFSC";
  char beta[5]="TELE";
  char delta[5]="RAC";
  char gamma[5]="CGER";

  vp[0] = alfa;
  vp[1] = beta;
  vp[2] = delta;
  vp[3] = gamma;  

  for(i=0;i<4;i++)
	printf("%s\n", vp[i]);
}

Fig1Aula24PrgITele.jpg

Observe que vp é um vetor de ponteiros para char e cada elemento aponta para uma cadeia de caracteres.

Argumentos de linha de comando

Um bom exemplo de vetor de ponteiros é a passagem de parâmetros na linha de comando. Cada parâmetro é tratado como uma cadeia de caracteres apontada por um elemento do vetor argv. O número de parâmetros é passado em argc. Note que argv[0] aponta para uma string que indica o nome do programa.

EXEMPLO
Teste o programa abaixo:
#include <stdio.h>

int main(int argc, char *argv[])
{
  int i;

  for (i=0;i<argc;i++) {
       printf("%s\n", argv[i]);
  }
  printf("Numero de parametros passados = %d\n", argc-1); /* o primeiro é o nome do arquivo executavél" */
}

Apontando para estruturas

Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura:

#include <stdio.h>

struct TRegistro {
   char nome[20];
   int idade;
} Tabela[4] = {
          {"joao",18,},
          {"maria",18,},
          {"jose",19,},
          {"lara",17,},
}
;

struct TRegistro *p;

void main()
{
  p = &Tabela[3]; /*p aponta para o registro 3 da tabela */
  printf("O nome na posição 3 é %s e idade = %d\n", p->nome,p->idade);
}
NOTE
Que o uso de p->nome é uma alternativa ao uso de (*p).nome

No primeiro caso pode-se ler: o campo nome do objeto que é apontado por p.

Retornando uma estrutura em uma função

No exemplo a abaixo a função RetornarStruct() retorna um ponteiro para uma estrutura. O cuidadado que se deve ter é que a função não deveria apontar para uma estrutura que foi criada localmente na função!

#include <stdio.h>
struct TRegistro {
   char nome[20];
   int idade;
} Tabela[4] = {
          {"joao",18,},
          {"maria",18,},
          {"jose",19,},
          {"lara",17,},
};

struct TRegistro *p;

struct TRegistro * RetornarStruct(int indice)
{
  return &Tabela[indice];
}

void main()
{
  p = RetornarStruct(2); /*p aponta para o registro 3 da tabela */
  printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}

Passando uma estrutura como parâmetro

#include <stdio.h>
struct TRegistro {
   char nome[20];
   int idade;
} Tabela[4] = {
          {"joao",18,},
          {"maria",18,},
          {"jose",19,},
          {"lara",17,},
};

struct TRegistro *p;

void MudarStruct(struct TRegistro *p1, int indice)
{
  Tabela[indice] = *p1;
}

void main()
{
  struct TRegistro aux = {"luisa",16};

  MudarStruct(&aux,2);
  p = &Tabela[2];
  printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}

Exercício: No programa acima construir uma função que imprime a Tabela usando ponteiros. A função deve receber como parâmetro um ponteiro para o início da tabela e o tamanho da tabela.

Exercícios

1. Implementar a função str_cat que concatena duas strings usando ponteiros.

2. Ordenar valores de um vetor de inteiros passando por referencia o ponteiro para esse vetor.

3. Implememtar um programa que recebe 3 parâmetros na linha de comando: dois números reais e um operador (char). Operador pode ser + ou menos. O programa deve mostrar o resultado da operação.

Exemplo: calcula 3.5 + 2.6
para usar a função atof para converter string em float.

4.Implementar um programa chamado cmpcadeia que testa se duas strings passadas na linha de comando são iguais. O programa deve imprimir uma mensagem indicando se são iguais ou diferentes. Usar a função strcmp da biblioteca. Caso sejam passados mais ou menos que dois parâmetros o programa deve se encerrar mostrando uma indicão do tipo:

cmpcadeia: dois parametros devem ser passados.

5. Renomeie o executável e veja seja a mensagem de erro mostra o nome correto do programa.

6. Implementar um programa para ler dados para dentro das variáveis x e y e somar o conteúdo das mesmas colocando o resultado em x SEM referenciar estas variáveis no scanf ou na expressão de soma. 2. Implementar uma função que compara duas strings passadas como parâmetro. A função retorna 0 se as strings forem iguais e 1 se diferentes. Usar ponteiros. 7. Implementar uma função que recebe como parâmetro o endereço de duas variáveis float que contêm a parte real e imaginária de um número complexo no formato polar (ângulo em radianos). A função deve converter do formato polar retangular colocando a coordenada x no primeira variável cujo endereço foi fornecido como parâmetro e a coordenada y na segunda variável (Olhar AULA 15).

void converte_polar_retang(float *parte1, float *parte2)
{
}

void main()
{
  float num1=1.5, num2=10.6;

  /*chamar a função aqui */

  /* imprimir os valores de num1 e num2 aqui */
}

8. Implemantar uma função que recebe como parâmetro o endereço de duas variáveis do tipo char e após a chamada da função os valores das variáveis devem ser convertidos para caixa alta (maiúsculas).

void main()
{
  char alfa='a', beta='b';

  capitaliza(&alfa, &beta);
  
  /* aqui os valores de alfa e beta deverão ser A e B */
}

9. Implementar uma função que recebe uma string contendo uma cadeia de caracteres com dígitos numéricos e retorna o valor inteiro da string. Usar ponteiros.

int a_toi(char *p)
{
}

void main()
{
  char *p="123";
  int x;

  x = a_toi(p);
  
  /* neste ponto x deve conter 123 */
}

10. O código abaixo implementa a série responsável pelo cálculo da raiz quadrada de um número.

#include <stdio.h>

float raiz_quadrada(float numero);

int main( int argc, char **argv)
{  
    float numero;
   
    printf("Entre com um número positivo por favor : ");
    scanf("%f",&numero);
   
    printf("A raiz quadrada de %.3f é %.5f \n",numero,raiz_quadrada(numero));

    return(0);
}


float raiz_quadrada (float numero)
{
    int n;
    float recorre = numero;
   
    for (n = 0; n < 10; ++n)
          recorre = recorre/2 + numero/(2*recorre);
           
    return(recorre);    
}

11. O código abaixo implementa uma variação em que número é passado por parâmetro através do programa principal e retorna a raiz quadrada de um número para o SO.

#include <stdio.h>
#include <stdlib.h>
 
float raiz_quadrada (float numero)
{
    int n;
    float recorre = numero;
 
    for (n = 0; n < 10; ++n)
          recorre = recorre/2 + numero/(2*recorre);
 
    return(recorre);    
}
 
int main( int argc, char *argv[])
{  
    float numero;

    if(argv[1]==NULL){
	printf("\n%s: falta número",argv[0]);
    }
    
    numero=atof(argv[1]);

    printf("\n%.4f \n",raiz_quadrada(numero));
 
    return(0);
}

Referências

[1] http://pw1.netcom.com/~tjensen/ptr/ch1x.htm

[2] http://eternallyconfuzzled.com/tuts/languages/jsw_tut_pointers.aspx

[3] http://duramecho.com/ComputerInformation/WhyCPointers.html

[4] http://boredzo.org/pointers/ http://boredzo.org/pointers/

[5] http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c600.html

[6] Videoaula | Ponteiros



Icone voltar.png Icone menu.png Icone prox.png