Desbravando o goto!

Introdução

O assunto dos gotos é um dos tópicos mais discutidos nos fóruns de programação. Várias críticas são reiteradas para a sua não utilização, mas será o goto assim tão maléfico? Não terá realmente a sua utilidade? Com este artigo espero convencê-lo que o goto, como uma ferramenta de programação que é, tem lugar na sua caixa de ferramentas.

O que é então o goto?

Para os mais distraídos o goto esta presente em diversas linguagens de programação. Esta ferramenta permite fazer saltos unidirecionais para locais específicos, quer estes sejam especificados pela linha de código em que se encontra ou por uma label.

goto label:

Na programação estruturada não temos qualquer obrigação em utilizador o goto.

Opiniões Divididas em relação ao goto!

Em 1969, Dijkstra  escreveu um artigo intitulado por Go to Statements Considered Harmful, e defendia a abolição do goto em todas as linguagens de alto nível. Dijkstra apresenta dois argumentos, um deles é que o goto complica a prova que uma certa parcela de código esta correta e o seu segundo argumento foi que complica a descrição do que o programa fez até um dado momento.

Por sua vez Donald E. Knuth escreveu um artigo chamado Structured Programming with go to Statements em que exemplifica as vantagens de usar goto em linguagens procedurais, como é o caso de C.

Usos do Goto!

As linguagens de alto nível atualmente possuem um sistema de garbage collection que facilitem a vida ao programador que não tem que perder tempo a se preocupar em dealocar o espaço de memória que foi usado. O goto é muito útil neste casos pois simplifica muito o código na leitura e elimina a necessidade de criação de funções para esse efeito o que implicaria possíveis problemas com scope. É também utilizado o goto em casos de error checking. Em outras linguagens de alto nível a próprio linguagem disponibiliza mecanismos que permitem efetuar este tipo de ação como é try...exception(excepção), no caso do Python. A título de curiosidade a implementação do return, break e continue utiliza o goto. Se analisarmos o código gerado pelo compilador veríamos que não há diferenças entre o goto e um if, e sem prejuízos de memória.

Utilizando os goto:

char *buf1, *buf2;
int err = OK;
 
if ((buf1 = malloc(100)) == NULL) {
  err = ERR_MALLOC;
  goto clean_up;
}
if (buf2 = malloc(200) == NULL ){
  err = ERR_MALLOC;
  goto clean_up2;
}

clean_up1:
  return err; /* Visto que falhou a alocação, não temos nada a libertar */
clean_up2:
  free(buf1); /* Partindo do principio que a alocação do buf2 é essencial para o funcionamento do programa */
  return err; 

Sem utilizar os goto:

char *buf1, *buf2
int err = OK; 
if ((buf = malloc(20)) == NULL) {
  err = ERR_MALLOC;
  return err;
}
if((buf2 = malloc(200)) == NULL){
  free(buf1);
  err = ERR_MALLOC;
  return err;
}

Neste pequeno exemplo observamos que a solução utilizando os goto permite um código mais simples de visualização. Este tipo de técnica está muito presente no código do kernel do Linux. Os programadores  entendem que esta é a melhor técnica para solucionar os seus problemas.

Um outro exemplo clássico do uso de goto é em ciclos  encadeados. Como por exemplo:

for(condição)
  for(condição)
    for(condição)
      goto final

Em alternativa ao goto podemos utilizar uma flag para sinalizar quando queremos sair do ciclo:

for(condição pretendida ){
  flag = 0;
  for(condição pretendida && !flag){
    for(condição pretendida && !flag){
      if(ação concluída)
        flag = 1;
    }
  }
}

Como podemos verificar com a utilização do goto podemos simplificar bastante o nosso código.

Maus usos do goto!

MENU:
switch(op) {
  case 1: goto ADD;
  case 2: goto SUB;
  case 3 : goto MULTI;
  case 4 : goto DIV;
  default: printf(“Por favor escolha uma opção valida\n”); goto MENU;
}

Uma opção mais simples seria:

while(1)
{
  if (op == 1)
    result = value1 + value2 /* valores pedidos previamente */
  if (op == 2)
    return = value1 – value 2 /* Verificações necesárias */
 /*....*/

Na última opção é mais fácil observarmos o que esta a acontecer. Se alguma vez utilizar o goto certifique se que estão presentes os critérios descritos em cima, se não for este o caso levante se vá beber/comer e volte a refazer, verá que me agradecera quando precisar de ler/modificar o seu código no futuro.

Conclusão!

Podemos então concluir, que o goto é apenas uma ferramenta e que a má reputação do goto se deve apenas à sua má utilização por parte do programador.

Com este artigo espero tê-lo esclarecido sobre a utilidade e os seus maus usos do goto. Com vista a expandirem o seu conhecimento, acerca do goto, para além deste artigo, o caminho eletrotécnico para o artigo do D. Knuth é disponibilizado no final.

Referências

  1. http://cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf
  2. http://dl.acm.org/citation.cfm?id=1241518

Publicado na edição 44 (PDF) da Revista PROGRAMAR.