Mudanças entre as edições de "AULA 20 - Programação 1 - Engenharia"
Linha 244: | Linha 244: | ||
<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 sma. | ||
+ | <syntaxhighlight lang=c> | ||
+ | #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; | ||
+ | } | ||
+ | |||
+ | |||
+ | 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; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
</li> | </li> | ||
<li> | <li> |
Edição das 19h50min de 24 de outubro de 2018
ALOCAÇÃO DINÂMICA DE MEMóRIA
Objetivos
- apresentar a anatomia de um programa em execução;
- apresentar a 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 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 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):
#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:
-
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.
#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; } 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; }
- 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.