Baixe o app para aproveitar ainda mais
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
Compartilhar