Mudanças entre as edições de "AULA 20 - Programação 1 - Engenharia"
(7 revisões intermediárias por 2 usuários não estão sendo mostradas) | |||
Linha 3: | Linha 3: | ||
===Objetivos=== | ===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=== | ===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<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 268: | Linha 270: | ||
printf("\nMatricula.....: %d\n", aluno.matricula); | printf("\nMatricula.....: %d\n", aluno.matricula); | ||
printf("Media do aluno: %.2f", media); | printf("Media do aluno: %.2f", media); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{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; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{collapse bottom}} | {{collapse bottom}} | ||
+ | |||
<syntaxhighlight lang=c> | <syntaxhighlight lang=c> | ||
#include <stdio.h> | #include <stdio.h> | ||
Linha 343: | 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 | + | 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 362: | 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 383: | 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:
-
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); }
-
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); }