Carlos Brando

Nome do Jogo

A Regra Direita-Esquerda do C

Atualmente estou envolvido em um projeto envolvendo módulos específicos em Ruby, Erlang e C. Ruby e Erlang são mais tranquilos, mas C tem a desagradável característica de deixar alguns códigos “meio” complicados de entender.

Porém, um colega da Plano Bê me enviou um artigo que me ajudou muito e quero compartilhá-lo com vocês. O artigo original parece ter sido escrito por Rick Ord da Universidade da California, em San Diego. Segue a tradução:

A Regra Direita-Esquerda do C

A regra direta-esquerda é muito útil para decifrar declarações na linguagem C e também pode ser de grande ajuda ao escrever código. A regra é bem simples, toda vez que encontrar os seguintes símbolos no código, leia:

*   como "um ponteiro para"      - sempre do lado esquerdo
[]  como "um array de"           - sempre do lado direito
()  como "uma função retornando" - sempre do lado direito

Passo 1

Encontre o identificador. Esse é o seu ponto de partida. Então, diga para si mesmo “o identificador é”. É nesse ponto que iniciamos a declaração.

Passo 2

Comece pelos símbolos a direita do identificador. Digamos que você encontrou um (), então você sabe que essa declaração é uma função. Então você deve dizer “o identificador é uma função retornando”. Caso você tenha encontrado um [], então você deve dizer “o identificador é um array de”. Continue avançando pela direita enquanto houver símbolos OU até encontrar um fecha parênteses ). (continue seguindo caso você encontre um abre parênteses, falaremos sobre isso mais abaixo.)

Passo 3

Quando terminarem os símbolos a direita do identificador, então comece a procurar por símbolos a sua esquerda. Se você encontrar algo diferente dos símbolos mencionados acima (digamos, algo como int), simplesmente repita o seu nome. Caso contrário, traduza o símbolo para o português usando a tabela. Continue seguindo pela esquerda enquanto houver símbolos OU até encontrar um abre parênteses (.

Então, repita os passos 2 e 3 até terminar toda a sua declaração. Veja abaixo alguns exemplos:

1
int *p[];
  1. Encontre o identificador.
1
2
3
int *p[];
     ^
"p é"
  1. Siga pela direita enquanto encontrar um símbolo ou abre parênteses.
1
2
3
int *p[];
      ^^
"p é um array de"
  1. Quando não puder mais mover para a direita (os símbolo acabaram), então siga para a esquerda e encontre:
1
2
3
int *p[];
    ^
"p é um array de ponteiros para"
  1. Continue seguinte pela esquerda e encontre:
1
2
3
4
int *p[];
^^^
"p é um array de ponteiros para int". 
(ou "p é um array onde cada elemento é um ponteiro para um inteiro")

Outro exemplo:

1
int *(*func())();
  1. Encontre o identificador.
1
2
3
int *(*func())();
       ^^^^
"func é"
  1. Vá para a direita.
1
2
3
int *(*func())();
           ^^
"func é uma função retornando"
  1. Não dá para ir mais a direita devido ao fecha parênteses, então vá para a esquerda.
1
2
3
int *(*func())();
      ^
"func é uma função retornando um ponteiro para"
  1. Não pode mais ir para a esquerda devido ao abre parênteses, então continue indo pela direita.
1
2
3
int *(*func())();
              ^^
"func é uma função retornando um ponteiro para uma função retornando"
  1. Não dá mais para ir para a direita porque não há mais simbolos, então vá para a esquerda.
1
2
3
int *(*func())();
    ^
"func é uma função retornando um ponteiro para uma função retornando um ponteiro para"
  1. E finalmente, o último símbolo a esquerda.
1
2
3
int *(*func())();
^^^
"func é uma função retornando um ponteiro para uma função retornando um ponteiro para um inteiro".

Como você pode ver, essa regra pode ser muito útil. Ela também é ótima para manter a sua sanidade enquanto estiver criando declarações, além de ajudá-lo a identificar onde colocar o próximo símbolo ou parênteses.

Algumas declarações podem parecer ainda mais complicadas quando definem o tamanho de um array ou informam uma lista de argumentos. Por exemplo, se você ver algo como [3], deve ler “um array (de tamanho 3) de…”. Mas se você ver algo como (char *,int), então deve ler “uma função esperando (char , int) e retornando…”*. Veja mais um exemplo:

1
int (*(*fun_one)(char *,double))[9][20];

Eu não vou seguir cada passo para decifrar essa declaração. Mas o resultado será: “fun_one é um ponteiro para uma função esperando (char , double) e retornando um ponteiro para um array (tamanho 9) de um array (tamanho 20) de inteiros”*.

Fica mais fácil se você remover o tamanho dos arrays e as listas de argumentos:

1
int (*(*fun_one)())[][];

Uma boa opção é decifrar dessa maneira e só depois adicionar o tamanho dos arrays e a lista de argumentos.

Algumas considerações finais

É totalmente possível criar declarações ilegais usando essa regra, então algum conhecimento do que é aceitável e do que não é em C é necessário. Por exemplo, se você tiver algo assim:

1
int *((*fun_one)())[][];

A declaração acima seria traduzida para “fun_one é um ponteiro para uma função retornando um array de arrays de ponteiros para inteiros”.

Uma vez que uma função não pode retornar um array, mas somente um ponteiro para um array, essa declaração é ilegal.

Combinações ilegais incluem:

[]() - não pode haver um array de funções
()() - não pode haver uma função que retorna uma função
()[] - não pode haver uma função que retorna um array

Em todos os casos acima, você provavelmente precisaria de um conjunto de parênteses para colocar um símbolo * a esquerda entre cada () e [] para tornar uma declaração válida.

Segue abaixo alguns exemplos válidos e inválidos para exercitar o que aprendemos:

int i;           "i um inteiro"
int *p;          "p é um ponteiro para um inteiro"
int a[];         "a é um array de inteiros"
int f();         "f é uma função retornando um inteiro"
int **pp;        "pp é um ponteiro para um ponteiro para um inteiro"
int (*pa)[];     "pa é um ponteiro para um array de inteiros"
int (*pf)();     "pf é um ponteiro para uma função retornando um inteiro"
int *ap[];       "ap é um array de ponteiros para inteiros"
int aa[][];      "aa é um array de arrays de inteiros"
int af[]();      "af é um array de funções retornando um inteiro (ILEGAL)"
int *fp();       "fp é uma função retornando um ponteiro para um inteiro"
int fa()[];      "fa é uma função retornando um array de inteiros (ILEGAL)"
int ff()();      "ff é uma função retornando uma função retornando um inteiro (ILEGAL)"
int ***ppp;      "ppp é um ponteiro para um ponteiro para um ponteiro para um inteiro"
int (**ppa)[];   "ppa é um ponteiro para um ponteiro para um array de inteiros"
int (**ppf)();   "ppf é um ponteiro para um ponteiro para uma função retornando um inteiro"
int *(*pap)[];   "pap é um ponteiro para um array de ponteiros para inteiros"
int (*paa)[][];  "paa é um ponteiro para um array de arrays de inteiros"
int (*paf)[]();  "paf é um ponteiro para um array de funções retornando um inteiro (ILEGAL)"
int *(*pfp)();   "pfp é um ponteiro para uma função retornando um ponteiro para um inteiro"
int (*pfa)()[];  "pfa é um ponteiro para uma função retornando um array de inteiros (ILEGAL)"
int (*pff)()();  "pff é um ponteiro para uma função retornando uma função retornando um inteiro (ILEGAL)"
int **app[];     "app é um array de ponteiros para ponteiros para um inteiro"
int (*apa[])[];  "apa é um array de ponteiros para um array de inteiros"
int (*apf[])();  "apf é um array de ponteiros para funções retornando um inteiro"
int *aap[][];    "aap é um array de arrays de ponteiros para inteiros"
int aaa[][][];   "aaa é um array de arrays de arrays de inteiros"
int aaf[][]();   "aaf é um array de arrays de funções retornando um inteiro (ILEGAL)"
int *afp[]();    "afp é um array de funções retornando um ponteiro para um inteiro (ILEGAL)"
int afa[]()[];   "afa é um array de funções retornando um array de inteiros (ILEGAL)"
int aff[]()();   "aff é um array de funções retornando funções retornando um inteiro (ILEGAL)"
int **fpp();     "fpp é uma função retornando um ponteiro para um ponteiro para um inteiro"
int (*fpa())[];  "fpa é uma função retornando um ponteiro para um array de inteiros"
int (*fpf())();  "fpf é uma função retornando um ponteiro para uma função retornando um inteiro"
int *fap()[];    "fap é uma função retornando um array de ponteiros para inteiros (ILEGAL)"
int faa()[][];   "faa é uma função retornando um array de arrays de inteiros (ILEGAL)"
int faf()[]();   "faf é uma função retornando um array de funções retornando inteiros (ILEGAL)"
int *ffp()();    "ffp é uma função retornando uma função retornando um ponteiro para um inteiro (ILEGAL)"

Comments