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 Stack Dados / texto Programa
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); 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
%[opções][largura mínima][.precisão][tamanho]conversão printf( valor do float double na na posição %p %p = = %f\n, %lf\n, %+06.2lf\n, ptr, ptr, *ptr); ptr, *ptr); tamanho 0 zeros à esquerda # alternativa - alinhar à esquerda + mostrar sinal positivo espaço para sinal positivo agrupar milhares I digitos alternativos tamanho hh char h short l long ll long long L long double z size_t t ptrdiff_t j intmax_t 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
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(); something_else(); } final_thing(); } K&R static char * concat(char *s1, char *s2) { while(x == y) Allman { something(); something_else(); } final_thing(); } static char * concat(char *s1, char *s2) { while(x == y) { something(); something_else(); } final_thing(); } GNU
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