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
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
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); }
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); }
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
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
Rotinas de entrada e saída não fazem parte
da linguagem
Disponíveis em bibliotecas que acompanham
os compiladores
Padronizadas
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
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
Caracteres especiais e reposicionamento do
cursor
printf(“barra invertida \\\n”); printf(“aspas duplas \”\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];
getchar lê um único caractere do terminal putchar(int c) imprime o caractere
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
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
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
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
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
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
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
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:
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
Se houver, preferir o estilo que já estiver em uso Underscore:
int num_clientes;
struct list *lista_alunos;
CamelCase:
int numClientes;
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