{"id":17,"date":"2008-09-13T05:02:37","date_gmt":"2008-09-13T05:02:37","guid":{"rendered":"http:\/\/suspensaodedescrenca.wordpress.com\/?p=17"},"modified":"2008-09-13T05:02:37","modified_gmt":"2008-09-13T05:02:37","slug":"tratamento-de-erros-em-c","status":"publish","type":"post","link":"https:\/\/suspensao.blog.br\/descrenca\/tratamento-de-erros-em-c\/","title":{"rendered":"Tratamento de erros em C com goto"},"content":{"rendered":"<p>Esses dias, come\u00e7ou-se a discutir na lista de discuss\u00e3o da Python Brasil <a title=\"Porque usar exce\u00e7\u00f5es?\" href=\"http:\/\/br.groups.yahoo.com\/group\/python-brasil\/message\/35063\">raz\u00f5es para se utilizar exce\u00e7\u00f5es<\/a>. Em um certo momento, um participante reconhecidamente competente comentou <a title=\"Porque usar exce\u00e7\u00f5es?\" href=\"http:\/\/br.groups.yahoo.com\/group\/python-brasil\/message\/35144\">o quanto \u00e9 dif\u00edcil tratar erros atrav\u00e9s do retorno de fun\u00e7\u00f5es, como em C<\/a>.<\/p>\n<p>Quando se tem um algoritmo complexo, cada opera\u00e7\u00e3o pass\u00edvel de erro implica em uma s\u00e9rie de <code>if<\/code>s para verificar se a opera\u00e7\u00e3o ocorreu corretamente. Se a opera\u00e7\u00e3o tiver falhado, ser\u00e1 necess\u00e1rio reverter todas as opera\u00e7\u00f5es anteriores para sair do algoritmo sem alterar o estado do programa.<\/p>\n<p>Vejamos um exemplo. Suponha que eu tenha a segunte <code>struct<\/code> para representar <em>arrays<\/em>:<\/p>\n<blockquote>\n<pre>typedef struct {\n        int size;\n        int *array;\n} array_t;<\/pre>\n<\/blockquote>\n<p>Agora, eu vou fazer uma fun\u00e7\u00e3o que l\u00ea, de um arquivo texto, o n\u00famero de elementos a ser posto em um desses <em>arrays<\/em> e, logo em seguida, os elementos. Essa fun\u00e7\u00e3o tamb\u00e9m vai alocar a <code>struct<\/code> do <em>array<\/em> e o <em>array de fato<\/em>. O problema \u00e9 que essa fun\u00e7\u00e3o \u00e9 bastante propensa a erros, pois podemos n\u00e3o conseguir<\/p>\n<ul>\n<li>abrir o arquvo dado;<\/li>\n<li>alocar a <code>struct<\/code>;<\/li>\n<li>ler o n\u00famero de elementos do arquvo dado, seja por erro de entrada\/sa\u00edda, seja por fim do arquivo;<\/li>\n<li>alocar mem\u00f3ria para guardar os elementos a serem lidos;<\/li>\n<li>ler um dos elementos, seja por erro de entrada\/sa\u00edda, seja por fim do arquivo.<\/li>\n<\/ul>\n<p>Complicado, n\u00e9? Note que, se conseguirmos abrir o arquivo mas n\u00e3o conseguirmos alocar a <code>struct<\/code>, temos de fechar o arquivo; se conseguirmos abrir o arquivo e alocar a <code>struct<\/code> mas n\u00e3o conseguirmos ler o n\u00famero de elementos do arquivo, temos de dealocar a <code>struct<\/code> e fechar o arquivo; e assim por diante. Assim sendo, se verificarmos todos os erros e adotarmos a tradi\u00e7\u00e3o de, em caso de erro, retornar <code>NULL<\/code>, nossa fun\u00e7\u00e3o seria mais ou menos assim:<\/p>\n<blockquote>\n<pre>array_t *readarray(const char *filename) {\n        FILE *file;\n        array_t *array;\n        int i;\n\n        file = fopen(filename, \"r\");\n        if (file == NULL) return NULL;\n\n        array = malloc(sizeof(array_t));\n        if (array == NULL) {\n\t\tfclose(file);\n\t\treturn NULL;\n\t}\n\n        if (fscanf(file, \"%d\", &amp;(array-&gt;size)) == EOF) {\n\t\tfree(array);\n\t\tfclose(file);\n\t\treturn NULL;\n\t}\n\n        array-&gt;array = malloc(sizeof(int)*array-&gt;size);\n        if (array-&gt;array == NULL)  {\n\t\tfree(array);\n\t\tfclose(file);\n\t\treturn NULL;\n\t}\n\n        for (i = 0; i &lt; array-&gt;size; i++) {\n                if (fscanf(file, \"%d\", array-&gt;array+i) == EOF) {\n\t\t\tfree(array-&gt;array);\n\t\t\tfree(array);\n\t\t\tfclose(file);\n\t\t\treturn NULL;\n\t\t}\n        }\n        return array;\n}<\/pre>\n<\/blockquote>\n<p>De fato, bastante trabalhoso, e com muito c\u00f3digo repetido&#8230;<\/p>\n<p>Note, por\u00e9m, como h\u00e1 duas situa\u00e7\u00f5es no c\u00f3digo acima. Em uma, quando tenho duas opera\u00e7\u00f5es para reverter, preciso reverter primeiro a \u00faltima executada, e depois a anterior. Por exemplo, quando vou dealocar tanto a <code>struct<\/code> quanto o <em>array<\/em> de inteiros, preciso dealocar primeiro o <em>array<\/em> de inteiros e depois a <code>struct<\/code>. Se dealoco a <code>struct<\/code> primeiro. posso n\u00e3o conseguir dealocar o <em>array<\/em> posteriormente.<\/p>\n<p>Na outra situa\u00e7\u00e3o, a ordem n\u00e3o importa. Por exemplo, se vou dealocar a <code>struct<\/code> e fechar o arquivo, n\u00e3o importa em que ordem eu o fa\u00e7a. Isso implica que eu posso, tamb\u00e9m, reverter primeiro a \u00faltima opera\u00e7\u00e3o executada e depois a primeira opera\u00e7\u00e3o.<\/p>\n<p>Qual o sentido disso? Bem, na pr\u00e1tica, nunca vi uma situa\u00e7\u00e3o onde eu tenha de reverter primeiro a primeira opera\u00e7\u00e3o executada, depois a segunda e assim por diante. Isso significa que, quando fa\u00e7o as opera\u00e7\u00f5es <code>a()<\/code>, <code>b()<\/code>, <code>c()<\/code> etc. a maneira &#8220;natural&#8221; de revert\u00ea-las \u00e9 chamando os reversores de tr\u00e1s para frente, mais ou menos como:<\/p>\n<blockquote><p><code>a();<br \/>\nb();<br \/>\nc();<br \/>\n\/* ... *\/<br \/>\nrevert_c();<br \/>\nrevert_b();<br \/>\nrevert_a();<\/code><\/p><\/blockquote>\n<p>Agora, vem o pulo do gato. No c\u00f3digo acima, ap\u00f3s cada opera\u00e7\u00e3o, vamos colocar um <code>if<\/code> para verificar se ela falhou ou n\u00e3o. Se falhou, executar-se-\u00e1 um <code>goto<\/code> para o reversor da \u00faltima opera\u00e7\u00e3o bem sucedida:<\/p>\n<blockquote><p><code>a();<br \/>\nif (failed_a()) goto FAILED_A;<br \/>\nb();<br \/>\nif (failed_b()) goto FAILED_B;<br \/>\nc();<br \/>\nif (failed_c()) goto FAILED_C;<br \/>\n\/* ... *\/<br \/>\nrevert_c();<br \/>\nFAILED_C:<br \/>\nrevert_b();<br \/>\nFAILED_B:<br \/>\nrevert_a();<br \/>\nFAILED_A:<br \/>\nreturn;<\/code><\/p><\/blockquote>\n<p>Se\u00a0 <code>a()<\/code> falhar, o algoritmo retorna; se\u00a0 <code>b()<\/code> falhar, o algoritmo vai para <code> FAILED_B:<\/code>, reverte\u00a0 <code>a()<\/code> e retorna; se <code>c()<\/code> falhar, o algoritmo vai para <code> FAILED_C<\/code>, reverte <code>b()<\/code>, reverte\u00a0 <code>a()<\/code> e retorna. Consegue ver o padr\u00e3o?<\/p>\n<p>Pois bem, se aplicarmos esse padr\u00e3o \u00e0 nossa fun\u00e7\u00e3o <code>readarray()<\/code> o resultado ser\u00e1 algo como:<\/p>\n<blockquote>\n<pre>array_t *readarray(const char *filename) {\n        FILE *file;\n        array_t *array;\n        int i;\n\n        file = fopen(filename, \"r\");\n        if (file == NULL) goto FILE_ERROR;\n\n        array = malloc(sizeof(array_t));\n        if (array == NULL) goto ARRAY_ALLOC_ERROR;\n\n        if (fscanf(file, \"%d\", &amp;(array-&gt;size)) == EOF)\n                goto SIZE_READ_ERROR;\n\n        array-&gt;array = malloc(sizeof(int)*array-&gt;size);\n        if (array-&gt;array == NULL) goto ARRAY_ARRAY_ALLOC_ERROR;\n\n        for (i = 0; i &lt; array-&gt;size; i++) {\n                if (fscanf(file, \"%d\", array-&gt;array+i) == EOF)\n                        goto ARRAY_CONTENT_READ_ERROR;\n        }\n        return array;\n\n        ARRAY_CONTENT_READ_ERROR:\n        free(array-&gt;array);\n        ARRAY_ARRAY_ALLOC_ERROR:\n        SIZE_READ_ERROR:\n        free(array);\n        ARRAY_ALLOC_ERROR:\n        fclose(file);\n        FILE_ERROR:\n        return NULL;\n}<\/pre>\n<\/blockquote>\n<p>Quais as vantagens desse padr\u00e3o? Bem, ele reduz a repeti\u00e7\u00e3o de c\u00f3digo de revers\u00e3o de opera\u00e7\u00f5es e separa o c\u00f3digo de tratamento de erro da l\u00f3gica da fun\u00e7\u00e3o. Na verdade, apesar de eu achar exce\u00e7\u00f5es o melhor m\u00e9todo de tratamento de erros moderno, para tratamento de erros <em>in loco<\/em> (dentro da pr\u00f3pria fun\u00e7\u00e3o) eu acho esse m\u00e9todo muito mais pr\u00e1tico.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Esses dias, come\u00e7ou-se a discutir na lista de discuss\u00e3o da Python Brasil raz\u00f5es para se utilizar exce\u00e7\u00f5es. Em um certo momento, um participante reconhecidamente competente comentou o quanto \u00e9 dif\u00edcil tratar erros atrav\u00e9s do retorno de fun\u00e7\u00f5es, como em C. Quando se tem um algoritmo complexo, cada opera\u00e7\u00e3o pass\u00edvel de erro implica em uma s\u00e9rie [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[12],"tags":[79,39,40,61],"class_list":["post-17","post","type-post","status-publish","format-standard","hentry","category-programacao","tag-c","tag-excecao","tag-goto","tag-tratamento-de-erro"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p23QLV-h","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts\/17","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/comments?post=17"}],"version-history":[{"count":0,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts\/17\/revisions"}],"wp:attachment":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/media?parent=17"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/categories?post=17"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/tags?post=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}