Mudanças entre as edições de "AULA 19 - Programação 1 - Graduação"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(15 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 37: Linha 37:
 
*[http://www.cplusplus.com/reference/cstdlib/free/?kw=free free]: libera memória;
 
*[http://www.cplusplus.com/reference/cstdlib/free/?kw=free free]: libera memória;
 
*[http://www.cplusplus.com/reference/cstdlib/realloc/ realloc]: realoca memória
 
*[http://www.cplusplus.com/reference/cstdlib/realloc/ realloc]: realoca memória
*[http://pt.wikipedia.org/wiki/Calloc calloc]:
+
*[http://pt.wikipedia.org/wiki/Calloc calloc]: aloca n bytes de memória e zera.
  
 
http://www.linuxjournal.com/article/4681
 
http://www.linuxjournal.com/article/4681
 
http://www.cs.cmu.edu/~guna/15-123S11/Lectures/Lecture08.pdf
 
http://www.cs.cmu.edu/~guna/15-123S11/Lectures/Lecture08.pdf
  
Exemplo 1: Alocação dinâmica de números inteiros (exercício puramente didático):
+
;EXEMPLO 1: Alocação dinâmica de números inteiros (exercício puramente didático):
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
Linha 63: Linha 63:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
;EXEMPLO 2: Diferente da função '''malloc''', '''calloc''' além de inicializar os espaços de memória ainda atribui o valor 0 (zero) para cada um deles.
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
 +
int main(void){
 +
 +
    int *valores, *aux;
 +
    int qtd, i, vlr;
 +
 +
    printf("\nEntre com a quantidade de números: ");
 +
    scanf("%d", &qtd);
 +
 +
    if(qtd == 0)
 +
        exit(0);
 +
 +
//Aloca um vetor com a quantidade recebida em qtd
 +
    valores = (int *) calloc(qtd, sizeof (int));
 +
 +
//Imprime o endereco e valor (zero) para a quantidade de números informada
 +
    aux = valores;
 +
    for(i = 1; i <= qtd; i++){
 +
        printf("%p\t%d\n", aux, *aux);
 +
        aux++;
 +
    }
 +
 +
    aux = valores; //Ponteiro para o primeiro espaço de memória
 +
    for(i = 1; i <= qtd; i++){
 +
        printf("Digite o número %d ->: ", i);
 +
        scanf("%d", &vlr);
 +
        *aux = vlr;
 +
        aux++;
 +
    }
 +
 +
    aux = valores; //Retorna à posição inicial no mapa de memória
 +
    for(i = 1; i <= qtd; i++){
 +
        printf("%p\t%d\n", aux, *aux);
 +
        aux++;
 +
    }
 +
 +
    free(valores);
 +
 +
    return 0;
 +
}
 +
</syntaxhighlight>
 +
 +
 +
 +
;EXEMPLO 3: Vejamos comos diferentes elementos do programa são armazenados quando esse programa é executado. Os comentários no código explicam onde as variáveis ficam armazenadas.
 +
 +
<syntaxhighlight lang=c>
 +
int bss1;
 +
static double bss2;
 +
char *bss3;
 +
// estas variáveis são armazenadas no segmento de dados não inicializado (BSS)
 +
int init1=0;
 +
float init2=10.0;
 +
char *init3="Alo mundo!";
 +
// estas variáveis são armazenadas no segmento de dados inicializados (DATA)
 +
// o código para função principal é armazenado em (code segment)
 +
int main()
 +
{
 +
int local1=10; // esta variável é armazenada na pilha (stack) e código de inicialização é gerado pelo compilador.
 +
int local2; //esta variável não é inicializada, portanto, tem valor de lixo. Ela não fica inicializada com zero.
 +
static int local3; // esta é alocada no segmento BSS e é inicializada com zero.
 +
static int local4=100; //este é alocada e inicializada no segmento de dados
 +
int *local5=malloc(sizeof(int));
 +
// esta variável é alocada na pilha no entanto, aponta para onde é alocada dinamicamente pelo bloco no heap.
 +
return 0;
 +
// stack frame para a função principal é destruída depois de executado o programa principal.
 +
}
 +
</syntaxhighlight>
 +
 +
===Usando a ferramenta '''nm'''===
 +
Há várias ferramentas para verificar onde uma variável é armazenada na memória. Mas a ferramenta fácil de usar é '''nm'''. Compile o programa acima (EXEMPLO 2) como indicado abaixo:
 +
 +
;Usando a ferramenta nm
 +
:$ gcc codigo.c -o teste
 +
:$ nm ./teste
 +
 +
Se nenhum argumento for passado para nm, o programa assume a entrada como a.out e vamos obter alguma saída como abaixo.
 +
 +
<code>
 +
08049f28 d _DYNAMIC
 +
08049ff4 d _GLOBAL_OFFSET_TABLE_
 +
080484dc R _IO_stdin_used
 +
        w _Jv_RegisterClasses
 +
08049f18 d __CTOR_END__
 +
08049f14 d __CTOR_LIST__
 +
08049f20 D __DTOR_END__
 +
08049f1c d __DTOR_LIST__
 +
080485e0 r __FRAME_END__
 +
08049f24 d __JCR_END__
 +
08049f24 d __JCR_LIST__
 +
0804a020 A __bss_start
 +
0804a00c D __data_start
 +
08048490 t __do_global_ctors_aux
 +
08048360 t __do_global_dtors_aux
 +
0804a010 D __dso_handle
 +
        w __gmon_start__
 +
08048482 T __i686.get_pc_thunk.bx
 +
08049f14 d __init_array_end
 +
08049f14 d __init_array_start
 +
08048480 T __libc_csu_fini
 +
08048410 T __libc_csu_init
 +
        U __libc_start_main@@GLIBC_2.0
 +
0804a020 A _edata
 +
0804a044 A _end
 +
080484bc T _fini
 +
080484d8 R _fp_hw
 +
080482b4 T _init
 +
08048330 T _start
 +
0804a040 B bss1
 +
0804a030 b bss2
 +
0804a03c B bss3
 +
0804a020 b completed.6159
 +
0804a00c W data_start
 +
0804a024 b dtor_idx.6161
 +
080483c0 t frame_dummy
 +
0804a028 B init1
 +
0804a014 D init2
 +
0804a018 D init3
 +
0804a038 b local3.1236
 +
0804a01c d local4.1237
 +
080483e4 T main
 +
        U malloc@@GLIBC_2.0
 +
</syntaxhighlight>
 +
 +
Os números são os valores de um símbolo, seguido do tipo de símbolo (apresentado como uma letra) e o nome do símbolo.
 +
O tipo de símbolo requer mais explicações:  Uma letra '''minúsculas''' significa que é '''variável local''' e '''maiúsculas''' significa '''global''' (externamente disponíveis a partir do arquivo).
 +
:B Seção de dados não inicializados (BSS)
 +
:D Seção de dados inicializados
 +
:T Seção de texto/código
 +
:U não identificado
  
 
==Alocando uma estrutura ==
 
==Alocando uma estrutura ==
Linha 92: Linha 228:
 
    
 
    
 
   teste=NULL;
 
   teste=NULL;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Usando o ''typedef'' para ajudar na definição e declaração de estruturas===
+
==Usando o ''typedef'' para ajudar na definição e declaração de estruturas==
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
Linha 168: Linha 305:
 
! style="background: #cdc5bf;" | [[AULA 18 - Programação 1 - Graduação | << ]]
 
! style="background: #cdc5bf;" | [[AULA 18 - Programação 1 - Graduação | << ]]
 
! style="background: #cdc5bf;" | Aula 19
 
! style="background: #cdc5bf;" | Aula 19
! style="background: #cdc5bf;" | [[PRG1-_Programação_I_-_Graduação | <> ]]
+
! style="background: #cdc5bf;" | [[AULA 20 - Programação 1 - Graduação | >> ]]
 
|}
 
|}

Edição atual tal como às 10h07min de 26 de maio de 2015

Alocação Dinâmica de Memória

Objetivos

  • Anatomia de um programa em execução;
  • Alocação dinâmica de memória.

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: 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 incializados). 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.

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):

  • malloc: aloca n bytes de memória;
  • free: libera memória;
  • realloc: realoca memória
  • calloc: aloca n bytes de memória e zera.

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

EXEMPLO 1
Alocação dinâmica de números inteiros (exercício puramente didático):
#include <stdlib.h>

main()
{
  int *px, *py;
  int resultado;
  px = (int *) malloc(sizeof(int));
  *px = 5;
  py = (int *) malloc(sizeof(int));
  *py = 2;
  resultado = *px + *py;

  free (px);
  px = NULL;
  free (py);
  py = NULL;
}
EXEMPLO 2
Diferente da função malloc, calloc além de inicializar os espaços de memória ainda atribui o valor 0 (zero) para cada um deles.
#include <stdio.h>
#include <stdlib.h>
 
int main(void){
 
    int *valores, *aux;
    int qtd, i, vlr;
 
    printf("\nEntre com a quantidade de números: ");
    scanf("%d", &qtd);
 
    if(qtd == 0)
        exit(0);

//Aloca um vetor com a quantidade recebida em qtd
    valores = (int *) calloc(qtd, sizeof (int));

//Imprime o endereco e valor (zero) para a quantidade de números informada
    aux = valores;
    for(i = 1; i <= qtd; i++){
        printf("%p\t%d\n", aux, *aux);
        aux++;
    }
 
    aux = valores; //Ponteiro para o primeiro espaço de memória
    for(i = 1; i <= qtd; i++){
        printf("Digite o número %d ->: ", i);
        scanf("%d", &vlr);
        *aux = vlr;
        aux++;
    }
 
    aux = valores; //Retorna à posição inicial no mapa de memória
    for(i = 1; i <= qtd; i++){
        printf("%p\t%d\n", aux, *aux);
        aux++;
    }
 
    free(valores);
 
    return 0;
}


EXEMPLO 3
Vejamos comos diferentes elementos do programa são armazenados quando esse programa é executado. Os comentários no código explicam onde as variáveis ficam armazenadas.
int bss1;
static double bss2;
char *bss3;
// estas variáveis são armazenadas no segmento de dados não inicializado (BSS)
int init1=0;
float init2=10.0;
char *init3="Alo mundo!";
// estas variáveis são armazenadas no segmento de dados inicializados (DATA)
// o código para função principal é armazenado em (code segment)
int main()
{
	int local1=10; // esta variável é armazenada na pilha (stack) e código de inicialização é gerado pelo compilador.
	int local2; //esta variável não é inicializada, portanto, tem valor de lixo. Ela não fica inicializada com zero.
	static int local3; // esta é alocada no segmento BSS e é inicializada com zero.
	static int local4=100; //este é alocada e inicializada no segmento de dados
	int *local5=malloc(sizeof(int));
// esta variável é alocada na pilha no entanto, aponta para onde é alocada dinamicamente pelo bloco no heap.
return 0;
// stack frame para a função principal é destruída depois de executado o programa principal.
}

Usando a ferramenta nm

Há várias ferramentas para verificar onde uma variável é armazenada na memória. Mas a ferramenta fácil de usar é nm. Compile o programa acima (EXEMPLO 2) como indicado abaixo:

Usando a ferramenta nm
$ gcc codigo.c -o teste
$ nm ./teste

Se nenhum argumento for passado para nm, o programa assume a entrada como a.out e vamos obter alguma saída como abaixo.

08049f28 d _DYNAMIC 08049ff4 d _GLOBAL_OFFSET_TABLE_ 080484dc R _IO_stdin_used

        w _Jv_RegisterClasses

08049f18 d __CTOR_END__ 08049f14 d __CTOR_LIST__ 08049f20 D __DTOR_END__ 08049f1c d __DTOR_LIST__ 080485e0 r __FRAME_END__ 08049f24 d __JCR_END__ 08049f24 d __JCR_LIST__ 0804a020 A __bss_start 0804a00c D __data_start 08048490 t __do_global_ctors_aux 08048360 t __do_global_dtors_aux 0804a010 D __dso_handle

        w __gmon_start__

08048482 T __i686.get_pc_thunk.bx 08049f14 d __init_array_end 08049f14 d __init_array_start 08048480 T __libc_csu_fini 08048410 T __libc_csu_init

        U __libc_start_main@@GLIBC_2.0

0804a020 A _edata 0804a044 A _end 080484bc T _fini 080484d8 R _fp_hw 080482b4 T _init 08048330 T _start 0804a040 B bss1 0804a030 b bss2 0804a03c B bss3 0804a020 b completed.6159 0804a00c W data_start 0804a024 b dtor_idx.6161 080483c0 t frame_dummy 0804a028 B init1 0804a014 D init2 0804a018 D init3 0804a038 b local3.1236 0804a01c d local4.1237 080483e4 T main

        U malloc@@GLIBC_2.0

</syntaxhighlight>

Os números são os valores de um símbolo, seguido do tipo de símbolo (apresentado como uma letra) e o nome do símbolo. O tipo de símbolo requer mais explicações: Uma letra minúsculas significa que é variável local e maiúsculas significa global (externamente disponíveis a partir do arquivo).

B Seção de dados não inicializados (BSS)
D Seção de dados inicializados
T Seção de texto/código
U não identificado

Alocando 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;
}

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

#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;
  
}

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ÍCIO
Refazer o exemplo anterior para que a quantidade de memória a ser alocada pela tabela seja passada na linha de comando.



<< Aula 19 >>