Buscar

Alocação Dinâmica de Memória em C

Prévia do material em texto

 Preferível sempre que não soubermos quanta 
memória um programa utilizará 
 Alocação estática é fixa 
▪ Definida durante a compilação 
 Alocação dinâmica permite utilizar a quantidade 
necessária de memória sem desperdícios 
▪ Definida durante a execução 
 
 
 Memória é dividida em duas áreas 
 Stack: Utilizada para alocação estática 
 Heap: Utilizada para alocação dinâmica 
 
Heap 
Memória 
Livre 
Programa 
Dados / texto 
 
Stack 
 Alocar um novo bloco de memória 
 malloc(), calloc() 
 Redimensionar um bloco já alocado 
 realloc() 
 Liberar um bloco alocado 
 free() 
 
 malloc aloca um bloco de size bytes 
 O conteúdo do bloco alocado é indeterminado 
 Retorna um ponteiro para void 
 O programador decide como usar o bloco alocado 
 Retorna NULL em caso de erro 
int *primos = malloc(7 * sizeof(int)); 
if(!primos) { perror(NULL); exit(1); } 
char *buffer = malloc(64); 
if(!buffer) { perror(NULL); exit(1); } 
double *fracoes = malloc(64); 
if(!fracoes) { perror(NULL); exit(1); } 
memset(fracoes, 0, 8*sizeof(double)); 
int *primos = malloc(7 * sizeof(int)); 
if(!primos) { perror(NULL); exit(1); } 
char *buffer = malloc(64); 
if(!buffer) { perror(NULL); exit(1); } 
double *fracoes = malloc(8*sizeof(double)); 
if(!fracoes) { perror(NULL); exit(1); } 
memset(fracoes, 0, 8*sizeof(double)); 
 calloc aloca um bloco com espaço para 
count objetos de tamanho size bytes 
 O bloco alocado é inicializado com zero 
int *primos = calloc(7, sizeof(int)); 
if(!primos) { perror(NULL); exit(1); } 
char *buffer = calloc(64, sizeof(char)); 
if(!buffer) { perror(NULL); exit(1); } 
double *fracoes = calloc(8, sizeof(double)); 
if(!fracoes) { perror(NULL); exit(1); } 
memset(fracoes, 0, 8*sizeof(double)); 
 Redimensiona o bloco de memória apontado 
por ptr para size bytes 
 Mantém o conteúdo do bloco apontado por ptr 
(limitado pelo tamanho do novo bloco) 
 O local do novo bloco de memória pode mudar 
▪ Mesmo se o novo bloco for menor que o anterior! 
int *primos = calloc(7, sizeof(int)); 
if(!primos) { perror(NULL); exit(1); } 
realloc(primos, 5*sizeof(int)); // BUG 
primos = realloc(primos, 5*sizeof(int)); // OK 
if(!primos) { perror(NULL); exit(1); } 
 Alocação estática: memória liberada pelo 
compilador 
 
 Alocação dinâmica: memória liberada pelo 
programador 
 Não liberar memória pode causar lentidão no 
computador ou fechamento do programa 
 Libera o bloco de memória apontado por ptr 
 Chamar free mais de uma vez pra um mesmo 
bloco de memória é um bug 
 free só pode ser chamada em ponteiros 
retornados por malloc, calloc e realloc. 
char * montar_string(struct endereco e) { 
 char *string = malloc(128); 
 if(!string) { perror(NULL); exit(1); } 
 // montar string ... 
 return string; 
} 
 
 dyn_alloc 
 dyn_lista_str 
 
 
 Rotinas de entrada e saída não fazem parte 
da linguagem 
 Disponíveis em bibliotecas que acompanham 
os compiladores 
 Padronizadas 
 Em C, são definidas no cabeçalho stdio.h 
 
 formato específica como os valores devem 
ser impressos na saída. 
 printf(“X = %d”, x); 
printf(“Area: %f\n”, PI*r*r); 
printf(“Nome: %s”, aluno.nome); 
 Existem vários caracteres de controle 
 Retorna o número de conversões impressas 
 Útil para checar erros 
conversão 
c char 
d int 
u unsigned int 
x int, hexadecimal 
f float 
e float, científico 
g float, e ou f 
p ponteiro 
s string 
% sinal percentual 
%[opções][largura mínima][.precisão][tamanho]conversão 
printf(“valor do float na posição %p = %f\n”, ptr, *ptr); 
tamanho 
hh char 
h short 
l long 
ll long long 
L long double 
z size_t 
t ptrdiff_t 
j intmax_t 
printf(“valor do double na posição %p = %lf\n”, ptr, *ptr); 
tamanho 
0 zeros à esquerda 
# alternativa 
- alinhar à esquerda 
+ mostrar sinal positivo 
espaço para sinal positivo 
„ agrupar milhares 
I digitos alternativos 
printf(“valor do double na posição %p = %+06.2lf\n”, ptr, *ptr); 
 Caracteres especiais e reposicionamento do 
cursor 
printf(“barra invertida \\\n”); 
printf(“aspas duplas \”\n”); 
printf(“tab\ttab\ttab\tnova linha\ntexto\n”); 
 scanf é o inverso do printf: 
lê dados do terminal 
 Mesmos códigos de conversão 
 Mesmas sequências de escape 
 Passar um ponteiro para a variável que você 
quer inicializar 
 int nlados = 0; 
float lado = 0; 
scanf(“%f %d\n”, &lado, &nlados); 
perimetro = lado * nlados; 
 Observe que scanf interrompe a leitura de 
um string (%s) quando encontra um branco 
 Especificadores de tamanho e filtro 
 %[aeiou]s lê apenas vogais 
 Para na primeira consoante, número, espaço, pontuação, etc 
 %[0123456789]s lê apenas números 
 %60s lê apenas 60 caracteres 
 %60[^0123456789]s lê até 60 caracteres parando 
quando encontrar um número 
char buffer[80]; 
scanf(“%79s”, buffer); 
 getchar lê um único caractere do terminal 
 putchar(int c) imprime o caractere 
com valor c no terminal 
 Mesma coisa, só precisamos passar o 
manipulador do arquivo como parâmetro 
 FILE *entrada; 
FILE *saida; 
... 
fscanf(entrada, “%79s”, buffer); 
char c = fgetc(entrada); 
fputc(c, saida); 
fprintf(saida, “X = %d”, x); 
 
 FILE * fopen(char *nome, char *modo) 
 Abre o arquivo com o dado nome 
 modo pode ser: 
▪ “r” para leitura, “w” para escrita, “rw” para leitura e escrita 
▪ Se o arquivo já existir, podemos usar “a” para adicionar ao arquivo 
 Sempre teste se o retorno é nulo, pois podem ocorrer erros 
▪ Arquivo ou caminho não existente, permissões insuficientes, etc. 
 int fclose(FILE *arquivo) 
 Fecha o arquivo apontado por arquivo 
FILE *arquivo = fopen(“C:\Users\Cunha\Desktop\teste.txt”, “w”); 
if(!arquivo) { perror(NULL); exit(EXIT_FAILURE); } 
fprintf(arquivo, “hello arquivo!\n”); 
fclose(arquivo); 
 printf e fprintf são idênticas, só operam sobre 
manipuladores de arquivos diferentes 
 printf sempre imprime na saída padrão (terminal) 
 fprintf recebe o arquivo onde imprimir como parâmetro 
 O manipulador do arquivo correspondente à saída padrão é o 
stdout, e o manipulador da entrada padrão é o stdin 
▪ fprintf(stdout, “imprimindo no terminal\n”); 
 Cuidado ao terminar de ler um arquivo 
 Use int feof(FILE *arquivo)para testar 
se já leu o arquivo até o fim 
▪ feof retorna falso se o arquivo ainda não tiver 
terminado 
▪ feof só retorna verdadeiro depois que você tentar ler 
após o arquivo ter terminado 
 Saber a posição atual do arquivo 
 long ftell(FILE *arquivo) 
 
 Mudar para uma dada posição no arquivo 
 int fseek(FILE *arquivo, 
 int base, int distancia) 
 Onde base pode ser: 
▪ SEEK_SET, o começo do arquivo 
▪ SEEK_CUR, a posição atual no arquivo 
▪ SEEK_END, o final do arquivo 
 
 Podemos ler uma linha de texto de um arquivo 
usando fgets 
 char buf[BUFSIZE]; 
fgets(buf, BUFSIZE, arquivo); 
 Depois processamos a linha usando sscanf 
 sscanf(buf, “%d %d %lf\n”, &int1, &int 
 Seguro: 
 Não tem como ler uma linha maior do que BUFSIZE 
 Retorna NULL se aconteceu algum erro 
 Lê linha a linha (evita problemas comums com scanf) 
 int main(int argc, char *argv[]) { ... } 
 argv é um arranjo de strings, um parâmetro em cada 
índice do arranjo 
 argc é o número de parâmetros em argv 
 argv[0] é sempre o nome do executável 
▪ Logo, argc >= 1 
 Para processamento avançado de parâmetros, 
use getopt() 
 Parâmetros em qualquer ordem, opcionais, etc. 
 ls -al --color=auto --sort=x 
 gcc –Wall–pedantic –std=gnu99 –o test test.c 
 Pequeno esforço, grande impacto 
 
 Código mais legível 
 Ajuda o entendimento das idéias 
 Útil quando você for ler o código 6 meses depois 
 Útil para outras pessoas 
 Código com menos erros 
 Economizar tempo e ter menos dor de cabeça 
 Em AEDS2: ajuda entendimento das idéias e 
correção dos trabalhos 
 Realça estrutura lógica do código 
 
 Em geral, indenta-se com tabulação (tab) 
 Em geral um tab corresponde a 8 espaços, mas é 
configurável na maioria dos editores 
static char * concat (char *s1, char *s2) { while (x == y) { something (); somethingelse (); } finalthing (); } static char * concat (char *s1, char *s2) { while (x == y) { something (); somethingelse (); } finalthing (); } 
static char * concat(char *s1, char *s2) 
{ 
 while(x == y) { 
 something(); 
 something_else(); 
 } 
 final_thing(); 
} 
static char * 
concat(char *s1, char *s2) 
{ 
 while(x == y) 
 { 
 something(); 
 something_else(); 
 } 
 final_thing(); 
} 
static char * 
concat(char *s1, char *s2) 
{ 
 while(x == y) 
 { 
 something(); 
 something_else(); 
 } 
 final_thing(); 
} 
GNU 
K&R 
Allman 
 Facilitam a compreensão do código, mais 
importantes para código complexo 
 
 Código bem escrito não depende muito de 
comentários 
 
 Comentário errado é pior do que nenhum 
comentário 
 Revisar comentários quando o código mudar 
 No início de um módulo 
 Descrever variáveis globais ou importantes 
 Em funções para explicar os parâmetros, o 
tipo de retorno, e o que a função faz 
 Não explicar como a função faz a tarefa, código 
deve ser claro o bastante 
 Indicar invariantes de loops 
 
 Não comentar o óbvio 
DUH: 
i += 1; // incrementa i. 
OK: 
i += 1; // compensar borda. 
 Escolher bons identificadores ajudam a 
compreensão do código 
 void ordena(int *vetor); 
 void processa(int *vetor); 
 double media(int *vetor); 
 Mesma coisa para variáveis 
▪ Variáveis auxiliares podem receber nomes simples, mas 
sem exagerar 
▪ Indices: i, j, k 
▪ Variáveis tipo ponto flutuante: x, y, z 
▪ Strings: s1, s2, str 
 Se houver, preferir o estilo que já estiver em uso 
 
 Underscore: 
 int num_clientes; 
 struct list *lista_alunos; 
 
 CamelCase: 
 int numClientes; 
 struct lista *listaAlunos; 
 Não usar “números mágicos” no código 
 Valores podem precisar ser modificados 
 “Números mágicos” não têm significado 
 Usar #define para dar nome a constantes 
 Nomes em maiúsculas 
#define PI 3.14159 
#define TAMANHO_MAX_LINHA 256 
 
char * le_linha(FILE *entrada) { 
 char *linha = malloc(TAMANHO_MAX_LINHA); 
 ... 
 return linha; 
} 
 Particionamento de um programa 
 Um módulo geralmente é um par de arquivos 
 modulo.c contém a implementação das funções 
 modulo.h contém a declaração das funções e 
tipos de dados; é importado por outros módulos 
 Outros programadores só precisam saber o 
que o módulo faz, não como ele funciona 
 Procurar identificar módulos independentes 
para que eles possam ser reaproveitados

Continue navegando