_Isis_ 202 Denunciar post Postado Maio 20, 2009 Original: http://strufts.blogspot.com Linguagens como C++, Java, C#, Python e Ruby possuem um mecanismo de tratamento de erros que envolve exceções. Adicionalmente, usa-se algum método para imprimir um stack trace, que informa onde ocorreu o erro. Em C, tudo deve ser resolvido com ifs e o método mais comum é avaliar o valor de retorno da função e imprimir uma mensagem dizendo que tal operação deu errado. Exemplo: se a tentativa de conectar a um servidor que suporta no máximo 1 cliente deu errado, imprimimos a mensagem "Servidor lotado.". O problema dessa abordagem é que temos retrabalho na maioria das vezes, já que as funções, quando não retornam somente um valor documentado indicando sucesso ou erro, atribuem um determinado valor a uma variável. Este "tutorial" visa mostrar outra abordagem da exibição de erros, ou, se você preferir, tratamento de erros. A VARIÁVEL ERRNO Comecemos com um código simples de leitura da entrada padrão. #include <stdio.h> #include <errno.h> int main(int argc, char const* argv[]) { int n; do { scanf("%d",&n); }while(!feof(stdin)); printf("%m\n"); return 0; } Esse código serve apenas para demonstrar um formatador do printf que pode ser usado para enviar mensagens ao usuário. Do manual da função: m (Glibc extension.) Print output of strerror(errno).No argument is required. Vemos que o formatador %m é uma extensão da Glibc. Se você está no Windows, deve utilizar a função strerror (C99), aqui definida em string.h, desse modo: #include <stdio.h> #include <errno.h> #include <string.h> int main(int argc, char const* argv[]) { int n; do { scanf("%d",&n); }while(!feof(stdin)); printf("%s\n",strerror(errno)); return 0; } Atente para o seguinte: a função strerror também pode atribuir um valor à variável errno nestas duas situações: 1-O valor passado como argumento é inválido. 2-Espaço insuficiente para armazenar a mensagem de erro. #include <stdio.h> #include <errno.h> #include <string.h> int main(int argc, char const* argv[]) { printf("%s\n",strerror(-123)); return 0; } Várias funções utilizam a variável errno para "comunicar" o resultado de uma operação. Mas atenção: NÃO DECLARE A VARIÁVEL ERRNO EM SEU PROGRAMA. O ISO C a define como um lvalue inteiro e que não deve ser explicitamente declarada. A variável é local a threads, ou seja, quando é definida por uma thread não afeta seu valor em outra thread. Portanto, ao invés de encher de printf("Erro aqui") ou print("%s\n",ERRO1), prefira o uso dessa variável. Se você está em um ambiente UNIX, para visualizar as mensagens de erro disponíveis pode executar o seguinte código. #include <stdio.h> #include <errno.h> int main(int argc, char const* argv[]) { printf("Valor de sys_nerr: %d\n",sys_nerr); int i; for(i=0;i<sys_nerr;i++) printf("%s\n",sys_errlist[i]); return 0; } Podemos ignorar os warnings sobre o uso de sys_errlist, já que queremos somente verificar a quantidade de erros cadastrada. Como o compilador indica, o uso explícito de sys_errlist deve ser evitado. Logo, a função perror deve ser utilizada somente para erros definidos pelo usuários (em defines, por exemplo), apesar de pertencer ao C99. EXTENSÕES GLIBC - ERROR.H Em Java temos a estrutura try-catch-finally para tratamento de exceções, sendo que o catch é utilizado, geralmente, para imprimir um stack trace do programa, contendo informações sobre o erro, tais como número de linha e nome da função. Se você está num ambiente Linux e seu programa não vai ser portável, pode utilizar o header error.h, contendo 2 funções: error e error_at_line. A função error aceita, no mínimo, 3 argumentos: o primeiro é um inteiro e, se diferente de zero, especifica o valor retornado pelo programa ao chamar a função exit (não é necessário que você chame a função exit explicitamente, pois error já faz isso se este argumento for diferente de zero) o segundo argumento é um inteiro que representa o valor do erro (aleatório existente em sys_errlist ou a variável errno) o terceiro argumento pode assumir duas formas: uma string (nesse caso a função fica restrita a 3 argumentos) ou um especificador de formato, igual ao do printf, seguido dos dados a imprimir. O que é impresso na tela depende do valor do segundo argumento e o comportamento do programa depende do valor do primeiro argumento. Quadro resumo: (STATUS-1º argumento, ERRNUM-2º argumento) (0;0) : Imprime o nome do programa seguido por ": " e a mensagem especificada no terceiro argumento, continuando a execução. (0;Diferente de 0) : Imprime o nome do programa seguido de ": ", da mensagem especificada no 3º argumento, mais ": " e o resultado de perror(errnum) (2º argumento), continuando a execução. (Diferente de 0;0) : Imprime o nome do programa seguido por ": " e a mensagem especificada no terceiro argumento, terminando a execução e retornando o valor do 1º argumento. (Diferente de 0; Diferente de 0) : Imprime o nome do programa seguido de ": ", da mensagem especificada no 3º argumento, mais ": " e o resultado de perror(errnum) (2º argumento), terminando a execução e retornando o valor do 1º argumento. A segunda função, error_at_line, tem comportamento semelhante ao da função error, mas com a inclusão do nome do arquivo e do número da linha. Seus argumentos são: 1- int status : valor de retorno do programa ao sair. A função exit só é chamada se status != 0. 2- int errnum : valor do erro (errno). 3- const char * filename : nome do arquivo. 4- unsigned int linenum : número da linha. 5- const char *format : igual ao 3º argumento da função error. Para o terceiro e quarto argumentos é bom utilizar os valores do pré-processador __FILE__ e __LINE__ quando estamos tratando dos erros do próprio programa, mas outros valores podem ser utilizados em casos como parsing de um arquivo inválido. #include <stdio.h> #include <errno.h> int main(int argc, char const* argv[]) { int n; do { scanf("%d",&n); }while(!feof(stdin)); error_at_line(0,errno,__FILE__,__LINE__,"%s %d","TESTE DE MENSAGEM",errno); puts("LINHA A MAIS"); return 0; } EXTENSÕES GNU - BACKTRACE Eu acho sacal debugar um programa com o GDB (não uso Nemiver e nem DDD) e gosto da história do printStackTrace do Java (apesar de não ir muito com o tamanho da API). Como o único lugar onde programo em C é Linux, posso me dar ao luxo de utilizar o header execinfo. Nele temos 3 funções para ter um backtrace do programa. A função backtrace aceita dois argumentos: um void** para um buffer onde será colocado o backtrace e um int que especifica a quantidade máxima de endereços void * armazenada no buffer. #include <stdio.h> #include <execinfo.h> void trace() { #define TAM 100 void *buffer[TAM]; int total_pointer = backtrace(buffer,TAM); int i; for(i=0;i<total_pointer;i++) printf("%x\n",buffer[i]); } void soma(int n) { if (n) soma(n-1); else trace(); } int main(int argc, char const* argv[]) { soma(3); return 0; } A backtrace armazena endereços no buffer passado como argumento. Para que seja possível entender algo precisamos usar a função backtrace_symbols, passando o buffer da função backtrace como primeiro argumento e um inteiro que especifica a quantidade de endereços nesse buffer. Esse valor é retornado pela função backtrace. #include <stdio.h> #include <execinfo.h> #include <stdlib.h> void trace() { #define TAM 100 void *buffer[TAM]; int total_pointer = backtrace(buffer,TAM); char ** strings = backtrace_symbols(buffer,total_pointer); int i; for(i=0;i<total_pointer;i++) printf("%s\n",strings[i]); free(strings); strings = NULL; } void soma(int n) { if (n) soma(n-1); else trace(); } int main(int argc, char const* argv[]) { soma(3); return 0; } Note a ausência de malloc para a variável char ** strings. A função backtrace_symbols aloca o espaço, sendo que o programador deve dar free depois de usar o array de strings. Para não precisar de malloc e nem de free, podemos usar a função backtrace_symbols_fd, que escreve o backtrace em um descritor de arquivo. #include <stdio.h> #include <execinfo.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> void trace() { #define TAM 100 void *buffer[TAM]; int total_pointer = backtrace(buffer,TAM); int fd = open("trace.txt",O_RDWR|O_CREAT); if (fd == -1) { printf("%s\n",strerror(errno)); exit(1); } else backtrace_symbols_fd(buffer,total_pointer,fd); } void soma(int n) { if (n) soma(n-1); else trace(); } int main(int argc, char const* argv[]) { soma(3); return 0; } DEBUGANDO O MALLOC DENTRO DO PROGRAMA Outras funções próprias do GNU, definidas no header mcheck. Podemos utilizar um arquivo para fazer o dump das chamadas do malloc, mas temos que exportar a variável MALLOC_TRACE. #include <mcheck.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char const* argv[]) { mtrace(); char *s=(char*)malloc(1); free(s); muntrace(); return 0; } Note que não é user-friendly. Para saber o que se passa usamos o comando mtrace. Nesse caso, vemos que não liberamos memória em algum ponto do programa. Quando liberamos toda a memória alocada, a mensagem "No memory leaks" é exibida. Compartilhar este post Link para o post Compartilhar em outros sites
VictorCacciari 42 Denunciar post Postado Maio 24, 2009 http://forum.imasters.com.br/public/style_emoticons/default/clap.gif http://forum.imasters.com.br/public/style_emoticons/default/clap.gif http://forum.imasters.com.br/public/style_emoticons/default/clap.gif http://forum.imasters.com.br/public/style_emoticons/default/clap.gif Compartilhar este post Link para o post Compartilhar em outros sites