Mudanças entre as edições de "AULA 20 - Programação 1 - Engenharia"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(6 revisões intermediárias por 2 usuários não estão sendo mostradas)
Linha 3: Linha 3:
 
===Objetivos===
 
===Objetivos===
  
*apresentar a anatomia de um programa em execução;
+
Após esta aula o aluno deverá:
*apresentar a alocação dinâmica de memória.
+
 
 +
*conhecer a anatomia de um programa em execução;
 +
*utilizar os mecanismos básicos de alocação dinâmica de memória do C.
  
 
===A área de heap e o layout de memória de um programa C===
 
===A área de heap e o layout de memória de um programa C===
Linha 232: Linha 234:
 
{{collapse top | Exemplo de uso do typedef}}
 
{{collapse top | Exemplo de uso do typedef}}
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
    #include<stdio.h>
+
#include<stdio.h>
    #include<stdlib.h>
+
#include<stdlib.h>
 
     //redefinindo os tipos básicos float e int
 
     //redefinindo os tipos básicos float e int
 
     typedef float nota;
 
     typedef float nota;
Linha 273: Linha 275:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
{{collapse bottom}}
 
{{collapse bottom}}
 +
 +
{{collapse top | Exemplo de ponteiro para Struct usando ->}}
 +
    #include<stdio.h>
 +
    #include<stdlib.h>
 +
 +
    //Usando typedef na struct para encurtar o comando na declaração
 +
    typedef struct
 +
    {
 +
      int matricula ;
 +
      float nota;
 +
    }tAluno;
 +
    int main (void)
 +
    {
 +
      //a1 é uma struct do tipo tAluno
 +
      tAluno a1 ;
 +
     
 +
      //*ptrAluno é um ponteiro do tipo tAluno que
 +
      //recebe o endereço de a1;
 +
      tAluno *ptrAluno = &a1; 
 +
     
 +
      //Atribuindo valores para os membros da struct a1
 +
      a1.matricula =555;
 +
      a1.nota = 8.0;
 +
     
 +
      //exibindo dados usando a struct diretamente usando .
 +
      printf ("matricula: %d nota: %.2f \n",a1.matricula,a1.nota);
 +
     
 +
      //Podemos atribuir ou acessar um valor usando o ponteiro (*ptrAluno)
 +
      (*ptrAluno).nota = 8.5;
 +
     
 +
      //exibindo dados usando um ponteiro para struct
 +
      printf ("\nmatricula: %d nota: %.2f \n",(*ptrAluno).matricula,(*ptrAluno).nota);
 +
     
 +
      //ptrAluno-> substitui a notação (*ptrAluno). de forma mais intuitiva 
 +
     
 +
      //atribuindo um novo valor para a nota usando ptrAluno->
 +
      ptrAluno->nota = 9.0;
 +
     
 +
      //portanto (*ptrAluno).nota é o mesmo que usar ptrAluno->nota
 +
     
 +
      printf ("\nmatricula: %d nota: %.2f \n",ptrAluno->matricula,ptrAluno->nota);
 +
      return 0;
 +
    }
 +
</syntaxhighlight>
 +
{{collapse bottom}}
 +
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
Linha 342: Linha 390:
 
<ol>
 
<ol>
 
   <li>
 
   <li>
     Criar uma função que recebe dois números complexos como parâmetro e retorna um ponteiro para uma área dinamicamente criada, contendo a soma destes números. A função retorna NULL caso não tenha sido possível alocar a área para a sma.
+
     Criar uma função que recebe dois números complexos como parâmetro e retorna um ponteiro para uma área dinamicamente criada, contendo a soma destes números. A função retorna NULL caso não tenha sido possível alocar a área para a soma.
 
{{collapse top | solução Ex. 1}}
 
{{collapse top | solução Ex. 1}}
<!--
 
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdlib.h>
 
#include <stdlib.h>
Linha 361: Linha 408:
 
   p = (struct tipo_complexo*) malloc(sizeof(struct tipo_complexo));
 
   p = (struct tipo_complexo*) malloc(sizeof(struct tipo_complexo));
  
   if (p!=NULL) {
+
   if (p != NULL) {
 
       p->real = a.real + b.real;
 
       p->real = a.real + b.real;
 
       p->imag = a.imag + b.imag;
 
       p->imag = a.imag + b.imag;
Linha 382: Linha 429:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
-->
 
 
{{collapse bottom}}
 
{{collapse bottom}}
 
   </li>
 
   </li>
 
   <li>
 
   <li>
 
     Criar uma função que recebe uma string passada como parâmetro e retorna um ponteiro para uma string (armazenada em uma área dinâmicamente criada) contendo uma cópia da string passada mas com caracteres capitalizados.  
 
     Criar uma função que recebe uma string passada como parâmetro e retorna um ponteiro para uma string (armazenada em uma área dinâmicamente criada) contendo uma cópia da string passada mas com caracteres capitalizados.  
 +
{{collapse top | solução Ex. 2}}
 +
<syntaxhighlight lang=c>
 +
//Autor: Victor C
 +
 +
#include <stdlib.h>
 +
#include <stdio.h>
 +
 +
 +
 +
 +
// depois criar um parâmetro na função
 +
char *func(char *string) {
 +
  char *p = (char *) malloc(50 * sizeof(char)); // 50 porque sao 50 espaços no vetor
 +
// que seria a "mesma" coisa que : string[50]
 +
  p=string; 
 +
  return p;
 +
}
 +
 +
 +
int main()
 +
{
 +
  char *p;
 +
  char string[50] = "IFSC-SJ";
 +
  p = func(string);
 +
  printf("%s",p);
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
 
   </li>
 
   </li>
 
</ol>
 
</ol>

Edição atual tal como às 07h23min de 9 de abril de 2021

ALOCAÇÃO DINÂMICA DE MEMÓRIA

Objetivos

Após esta aula o aluno deverá:

  • conhecer a anatomia de um programa em execução;
  • utilizar os mecanismos básicos de alocação dinâmica de memória do C.

A área de heap e o layout de memória de um programa C

Neste link podemos ter uma ideia da anatomia de um programa na memória do computador.

http://shivacherukuri.blogspot.com.br/2011/03/memory-layout-in-cdata-segmentbss-code.html

Podemos observar que existe as seguintes áreas:

  • TEXT: área onde está o código;
  • BSS (Block Start with Symbol): dados estaticamente alocados e não inicializados;
  • DATA: dados estaticamente alocados e inicializados;
  • STACK: área de pilha (variáveis locais);
  • HEAP: área de dados alocados dinamicamente.

Quando declaramos uma variável global da forma:

 int x;

a variável x é alocada em uma área chamada BSS (dados não inicializados). Note que x possui uma área de memória reservada a ela (4 bytes) e cuja existência é o tempo de vida do programa em execução. Esta aŕea de BSS é zerada no início do programa de forma que todas as variáveis ali definidas começarão com o valor 0.

Da mesma forma, uma variável global da forma:

 int y=10;

é alocada na área de DATA. A inicialização é definida normalmente na carga do programa. Os valores de inicialização são copiados para a área de DATA na carga do programa, a partir do arquivo executável.

Por vezes, o tamanho dos dados não são conhecidos antes da execução do programa. Neste caso, pode ser interessante criá-los dinamicamente e é neste ponto que entra a área de HEAP. Trata-se de uma área de memória, gerenciada a partir de funções da biblioteca do C.

As funções mais conhecidas são (http://en.wikipedia.org/wiki/C_dynamic_memory_allocation):

http://www.linuxjournal.com/article/4681 http://www.cs.cmu.edu/~guna/15-123S11/Lectures/Lecture08.pdf

Exemplo alocação dinâmica em C
#include <stdio.h>
#include <stdlib.h> //necessário para usar as funções malloc() e free()

int main(void)
{
  float *v; //definindo o ponteiro v
  int i, num_componentes;

  printf("Informe o numero de componentes do vetor\n");
  scanf("%d", &num_componentes);

  /* ------------- Alocando dinamicamente o espaço necessário -------------

  1 - Calcular o número de bytes necessários
  primeiramente multiplicamos o número de componentes do vetor pela
  quantidade de bytes que é dada pelo comando sizeof,
  portanto temos:
  num_componentes * sizeof(float)

  2 - Reservar a quantidade de memória
  usamos malloc para reservar essa quantidade de memória,
  então temos:
  malloc(num_componentes * sizeof(float))

  3 - Converter o ponteiro para o tipo de dados desejado
  como a função malloc retorna um ponteiro do tipo void,
  precisamos converter esse ponteiro para o tipo da nossa variável, no caso float,
  por isso usamos o comando de conversão explicita:
  (float *)

  4 - juntando tudo e atribuindo em v temos o comando abaixo: */

  v = (float *) malloc(num_componentes * sizeof(float));

  //Armazenando os dados em um vetor
  for (i = 0; i < num_componentes; i++){
    printf("\nDigite o valor para a posicao %d do vetor: ", i+1);
    scanf("%f",&v[i]);
  }

  // ------ Percorrendo o vetor e imprimindo os valores ----------
  printf("\n*********** Valores do vetor dinamico ************\n\n");

  for (i = 0;i < num_componentes; i++)
  {
    printf("%.2f\n",v[i]);
  }

  //liberando o espaço de memória alocado
  free(v);

  return 0;
}

Exemplo 1: Alocação dinâmica de números inteiros (exercício puramente didático):

#include <stdlib.h>

main()
{
  int *px, *py; /*variáveis ponteiros para inteiro criadas no STACK */
  int resultado; /* variável reasultado criada no STACK */

  px = (int *) malloc(sizeof(int)); /* alocada área para um inteiro na área de HEAP */
                                    /* ponteiro aponta para esta área */ 
  *px = 5;                          /* colocado 5 na área apontada por px */

  py = (int *) malloc(sizeof(int)); /* similara anterior para ponteiro py */
  *py = 2;

  resultado = *px + *py;            /* soma os dois valores inteiros que estava na área de HEAP
                                       e coloca o valor na variável resultado */

  free (px);                        /* libera a área no HEAP que estava apontada por px */
  px = NULL;                        /* zero o ponteiro para deixá-lo apontando para área indevida */
  free (py);
  py = NULL;
}

Observe o uso do operador sizeof para captura do tamanho do inteiro. Esta abordagem garante portabilidade.


Exemplo 2 - Retornando um ponteiro para uma área alocada dentro da função.

#include <stdio.h>
#include <stdlib.h>

/* a função retorna um ponteiro para inteiro, contendo o resultado da soma de a com b */
/* programa de caráter puramente demonstrativo */

int *soma_int(int a, int b)
{
  int *p;

  p = (int *)malloc(sizeof(int));
  *p = a+b;
  return p;  
}

main()
{
  int *meu_ptr;

  meu_ptr = soma_int(10,15);
  if (meu_ptr != NULL) {
     printf("valor de soma 10 + 15 = %d\n", *meu_ptr);
     free (meu_ptr);
     meu_ptr=NULL;
  }
}


Observe na sequência o problema que pode acontecer caso ja retorno para uma área indevida de memória.

A função retorna um ponteiro para uma área alocada na PILHA (STACK) que é uma espécie de área de rascunho para criação de variáveis locais... O C gera um warning mas gera um executável que produz resultados inesperados.

#include <stdio.h>
#include <stdlib.h>

/* a função retorna um ponteiro para inteiro, contendo o resultado da soma de a com b */
/* programa de caráter puramente demonstrativo */

int *soma_int(int a, int b)
{
  int x; /* variável local criada no STACK */

  x = a+b;
  return &x; /* retorna um ponteiro para uma área que será liberada  após a chamada da função */
}

main()
{
  int *meu_ptr1, *meu_ptr2; 

  meu_ptr1 = soma_int(10,15);
  meu_ptr2 = soma_int(20,35);
  printf("valor de soma 10 + 15 = %d\n", *meu_ptr1);
  printf("valor de soma 20 + 35 = %d\n", *meu_ptr2);
}

Alocando uma estrutura

No exemplo seguinte é alocada uma área dinâmica para uma estrutura.

#include <stdio.h>
#include <stdlib.h>
 
void main()
{
  struct TTeste{
     int x;
     int y;
  };
  
  struct TTeste *teste;
  

  
  teste = (struct TTeste *) malloc (sizeof(struct TTeste));
  if (teste==NULL) {
      printf("erro de alocação");
      exit(1);
  }
  
  teste->x=10;
  
  free(teste);
  
  teste=NULL;

Observe que o retorno da função malloc é void (para torná-la genérica). O retorno deve ser convertido devidamente usando uma operação de casting (struct TTeste *)

Usando o typedef para ajudar na definição e declaração de estruturas

A palavra chave typedef no C permite a criação de APELIDOS para tipos (ver https://en.wikipedia.org/wiki/Typedef):

Exemplo de uso do typedef
#include<stdio.h>
#include<stdlib.h>
    //redefinindo os tipos básicos float e int
    typedef float nota;
    typedef int inteiro;
    struct tAluno
    {
    //utilizando os tipos redefinidos dentro da struct
    inteiro matricula ;
    nota prova1;
    nota prova2;
    };
    //redefinindo a struct para encurtar o comando na declaração
    typedef struct tAluno tAluno ;
    int main (void)
    {
      /* declarando a variável do tipo tAluno
      note que com a redefinição agora não é mais necessário escrever struct tAluno */
      
      tAluno aluno ;
      
      nota media = 0;
      
      printf ("Informe o numero de matricula: ");
      scanf ("%d", &aluno.matricula);
      
      printf ("Informe a nota da primeira prova: ");
      scanf ("%f", &aluno.prova1);
      
      printf ("Informe a nota da segunda prova: ");
      scanf ("%f", &aluno.prova2);
      
      media = (aluno.prova1 + aluno.prova2) / 2;
      
      printf("\nMatricula.....: %d\n", aluno.matricula);
      printf("Media do aluno: %.2f", media);

      return 0;
    }
Exemplo de ponteiro para Struct usando ->
   #include<stdio.h>
   #include<stdlib.h>
   //Usando typedef na struct para encurtar o comando na declaração
   typedef struct
   {
     int matricula ;
     float nota;
   }tAluno;
   int main (void)
   {
     //a1 é uma struct do tipo tAluno
     tAluno a1 ; 
     
     //*ptrAluno é um ponteiro do tipo tAluno que
     //recebe o endereço de a1;
     tAluno *ptrAluno = &a1;   
     
     //Atribuindo valores para os membros da struct a1
     a1.matricula =555;
     a1.nota = 8.0;
     
     //exibindo dados usando a struct diretamente usando .
     printf ("matricula: %d nota: %.2f \n",a1.matricula,a1.nota);
     
     //Podemos atribuir ou acessar um valor usando o ponteiro (*ptrAluno)
     (*ptrAluno).nota = 8.5;
     
     //exibindo dados usando um ponteiro para struct
     printf ("\nmatricula: %d nota: %.2f \n",(*ptrAluno).matricula,(*ptrAluno).nota);
     
     //ptrAluno-> substitui a notação (*ptrAluno). de forma mais intuitiva  
     
     //atribuindo um novo valor para a nota usando ptrAluno->
     ptrAluno->nota = 9.0;
     
     //portanto (*ptrAluno).nota é o mesmo que usar ptrAluno->nota
     
     printf ("\nmatricula: %d nota: %.2f \n",ptrAluno->matricula,ptrAluno->nota);
     return 0;
   }

</syntaxhighlight>

#include <stdio.h>
#include <stdlib.h>
 
void main()
{
  typedef struct {
     int x;
     int y;
  } TTeste;
  
  TTeste *teste;
  

  
  teste = (TTeste *) malloc (sizeof(TTeste));
  if (teste==NULL) {
      printf("erro de alocação");
      exit(1);
  }
  
  teste->x=10;
  
  free(teste);
  
  teste=NULL;
  
}

Note que o uso do typedef simpificou o uso da estrutura no código.

Alocando dinamicamente uma tabela de estruturas

#include <stdio.h>
#include <stdlib.h>

void main()
{
  struct TTeste{
     int x;
     int y;
  } *teste;

  if ((teste = (struct TTeste *) malloc (100*sizeof(struct TTeste)))==NULL) {
      printf("erro de alocação");
      exit(1);
  }



  teste[10].x= 5;

  if ((teste = realloc(teste, 10000*sizeof(struct TTeste)))==NULL) {
      printf("erro de alocação");
      exit(1);
  }

  teste[9000].x=20;
  free(teste);

}

Exercícios:

  1. Criar uma função que recebe dois números complexos como parâmetro e retorna um ponteiro para uma área dinamicamente criada, contendo a soma destes números. A função retorna NULL caso não tenha sido possível alocar a área para a soma.
    solução Ex. 1
    #include <stdlib.h>
    #include <stdio.h>
    
    struct tipo_complexo {
       float real;
       float imag;
    };
    
    
    struct tipo_complexo *soma_complexo(struct tipo_complexo a, struct tipo_complexo b)
    {
      struct tipo_complexo *p;
    
      p = (struct tipo_complexo*) malloc(sizeof(struct tipo_complexo));
    
      if (p != NULL) {
          p->real = a.real + b.real;
          p->imag = a.imag + b.imag;
      }
    
      return p;
    }
    
    int main()
    {
      struct tipo_complexo alfa = {2.5, 3.6}, beta = {2.9, 8.7}, *p_result;
     
      p_result = soma_complexo(alfa, beta);
    
      printf("%f %f\n", p_result->real, p_result->imag);
      free(p_result);
      p_result = NULL;
    
      return (0);
    }
    
  2. Criar uma função que recebe uma string passada como parâmetro e retorna um ponteiro para uma string (armazenada em uma área dinâmicamente criada) contendo uma cópia da string passada mas com caracteres capitalizados.
    solução Ex. 2
    //Autor: Victor C
    
    #include <stdlib.h>
    #include <stdio.h>
    
    
    
    
    // depois criar um parâmetro na função
    char *func(char *string) {
      char *p = (char *) malloc(50 * sizeof(char)); // 50 porque sao 50 espaços no vetor
    // que seria a "mesma" coisa que : string[50]
      p=string;  
      return p;
     }
     
    
    int main()
    {
      char *p;
      char string[50] = "IFSC-SJ";
      p = func(string);
      printf("%s",p);
    }