Como você lê as declarações C?

Eu já ouvi falar de alguns methods, mas nenhum deles ficou preso. Pessoalmente tento evitar tipos complexos em C e tentar dividi-los em typedef de componente.

Agora estou enfrentando a manutenção de algum código legado de um chamado ‘programador de três estrelas’, e estou tendo dificuldades em ler alguns dos códigos *** [] [].

Como você lê declarações complexas de C?

Este artigo explica as 7 regras relativamente simples que permitem que você leia qualquer declaração C, se você estiver querendo ou precisando fazê-lo manualmente: http://www.ericgiguere.com/articles/reading-c-declarations.html

  1. Encontre o identificador. Este é o seu ponto de partida. Em um pedaço de papel, escreva “declarar identificador como”.
  2. Olhe para a direita. Se não houver nada lá, ou se houver um parêntese direito “)”, vá para a etapa 4.
  3. Agora você está posicionado em um descritor de matriz (colchete esquerdo) ou de function (parêntese esquerdo). Pode haver uma sequência desses, terminando com um parêntese direito inigualável ou com o fim do declarator (um ponto-e-vírgula ou um “=” para boot). Para cada descritor, leia da esquerda para a direita:

    • se um array vazio “[]”, escreva “array of”
    • se uma matriz com um tamanho, escreva “tamanho da matriz de”
    • se uma function “()”, escreva “function retornando”

    Parar no parêntese sem correspondência ou no final do declarator, o que ocorrer primeiro.

  4. Volte para a posição inicial e olhe para a esquerda. Se não houver nada lá, ou se houver um parêntese à esquerda “(“, vá para a etapa 6.
  5. Agora você está posicionado em um descritor de ponteiro, “*”. Pode haver uma sequência destes à esquerda, terminando com um parêntese esquerdo incomparável “(” ou o início do declarador. Lendo da direita para a esquerda, para cada descritor de ponteiro escreva “ponteiro para”. Pare em parênteses ou o início do declarator, o que ocorrer primeiro.
  6. Neste ponto você tem uma expressão entre parênteses ou o declarator completo. Se você tiver uma expressão com parênteses, considere-a como seu novo ponto de partida e retorne à etapa 2.
  7. Anote o especificador de tipo. Pare.

Se você está bem com uma ferramenta, então eu sugiro usar o programa cdecl : http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

Eu geralmente uso o que às vezes é chamado de ‘regra direita no sentido horário’. É assim:

  • Comece pelo identificador.
  • Vá imediatamente para a direita.
  • Em seguida, mova no sentido horário e vire para o lado esquerdo.
  • Mova no sentido horário e vá para o lado direito.
  • Faça isso desde que a declaração não tenha sido totalmente analisada.

Há uma meta-regra adicional que precisa ser resolvida:

  • Se houver parênteses, complete cada nível de parênteses antes de sair.

Aqui, ‘indo’ e ‘movendo’ em algum lugar significa ler o símbolo lá. As regras para isso são:

  • * – ponteiro para
  • () – function retornando
  • (int, int) – function tomando dois ints e retornando
  • int , char , etc. – int , char , etc.
  • [] – matriz de
  • [10] – matriz de dez
  • etc.

Então, por exemplo, int* (*xyz[10])(int*, char) é lido como:

xyz é um

matriz de dez

ponteiro para

function tomando um int * e um char e retornando

um int *

Uma palavra: cdecl

Droga, espancada por 15 segundos!

Cdecl (e c ++ decl) é um programa para codificação e decodificação de declarações de tipo C (ou C ++).

http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

Quando eu estava fazendo C, eu usei um programa chamado “cdecl”. Parece que está no Ubuntu Linux no pacote cutils ou cdecl, e provavelmente está disponível em outro lugar.

cdecl oferece uma interface de linha de comando, então vamos tentar:

 cdecl> explain int ***c[][] declare c as array of array of pointer to pointer to pointer to int 

outro exemplo

  explain int (*IMP)(ID,SEL) declare IMP as pointer to function (ID, SEL) returning int 

No entanto, há um capítulo inteiro sobre isso no livro “C Deep Secrets”, chamado “Unscrambling declaration in C.”

Atenciosamente Friedrich

Há também uma versão baseada na Web do cdecl, que é bem legal.

Problemas comuns de legibilidade incluem pointers de function e o fato de que os arrays são realmente pointers e que os arrays multidimensionais são realmente arrays de dimensão única (que são realmente pointers). Espero que ajude alguns.

Em qualquer caso, sempre que você entender as declarações, talvez você possa descobrir uma maneira de simplificá-las para torná-las mais legíveis para o próximo cara.

Solução automatizada é cdecl.

Em geral, você declara uma variável da maneira que você a usa. Por exemplo, você desrefere um ponteiro p como em:

 char c = * p

você o declara de maneira similar:

 char * p;

O mesmo vale para pointers de function peludos. Vamos declarar f como sendo um bom “ponteiro para retornar o ponteiro para int” e uma declaração externa apenas para ser engraçada. É um ponteiro para uma function, então começamos com:

 extern * f ();

Ele retorna um ponteiro para um int, então em algum lugar na frente há

 extern int * * f ();  // XXX ainda não está

Agora qual é a associatividade correta? Eu nunca consigo me lembrar, então use alguns parênteses.

 extern (int *) (* f) ();

Declare como você o usa.

Leia da direita para a esquerda.

 ***code[][] 
  • código [] [] é um array multidimensional
  • * código [] [] é um ponteiro de multidimensional array
  • ** código [] [] é um ponteiro de multidimensional array para um ponteiro
  • *** código [] [] é um ponteiro de multidimensional array para um ponteiro para um ponteiro

Acabou de encontrar uma seção iluminante em ” O Desenvolvimento da Linguagem C “:

Para cada object desse tipo composto, já havia uma maneira de mencionar o object subjacente: indexar o array, chamar a function, usar o operador de indireção no ponteiro. O raciocínio analógico levou a uma syntax de declaração para nomes espelhando a syntax da expressão na qual os nomes geralmente aparecem. Portanto,

int i, *pi, **ppi;

declara um inteiro, um ponteiro para um inteiro, um ponteiro para um ponteiro para um inteiro. A syntax dessas declarações reflete a observação de que i, * pi e ** ppi produzem um tipo int quando usados ​​em uma expressão. Similarmente,

int f(), *f(), (*f)();

declare uma function retornando um inteiro, uma function retornando um ponteiro para um inteiro, um ponteiro para uma function retornando um inteiro;

int *api[10], (*pai)[10];

declarar uma matriz de pointers para números inteiros e um ponteiro para uma matriz de inteiros. Em todos esses casos, a declaração de uma variável se assemelha ao seu uso em uma expressão cujo tipo é aquele nomeado na cabeça da declaração.