Design de codificação C - ponteiros de função?

24

Eu tenho um PIC18F46K22 e programo -o com o compilador XC8. No final, terei um sistema como um PC com stdine stdout. Portanto, no loop principal, haverá uma função que está verificando se há novas entradas. Se houver entrada, uma função será chamada de acordo. Por exemplo, quando eu insiro um A stdinativado, o PIC executará uma função como em function_Avez da function_Bqual é chamada quando insiro um B.

Quando o PIC for concluído com a função, desejo que a nova entrada seja enviada para a função. Portanto, ao pressionar A, o transmissor RS232 é aberto, a partir desse momento todas as entradas serão enviadas pelo RS232. No final, o projeto é um editor de texto independente. Portanto, ao pressionar A, o sistema de arquivos é aberto. A partir desse momento, você não está mais editando textos, mas procurando uma lista de arquivos. Isso significa que pressionar Cima e Baixo significa algo diferente do ambiente de edição de texto.

Pensei muito em como programar isso em C. Pensei nisso ontem à noite e gostaria de saber se é possível e, em caso afirmativo, como. O que eu quero fazer é:

  • A mainfunção chama uma função comofunction_A
  • function_Aaltera uma variável global function_addrpara o ponteiro de endereço da funçãoin_function_A
  • A partir desse momento, mainchama a função function_addrquando houver uma nova entrada.

Então, o que eu precisaria é de uma mainfunção que verifique se function_addré zero. Se for, uma função 'normal' deve ser chamada, como function_A. Caso contrário, a função at function_addrdeve ser chamada. Eu também preciso de um function_Aque muda function_addrpara um ponteiro para in_function_A.

Nota: quando a função do sistema de arquivos deve ser fechada, is_function_Abasta mudar function_addrpara 0.

Então, basicamente, minha pergunta é como posso

  • Obter o endereço de uma função (e armazená-lo em uma variável)
  • Chamar uma função em um endereço especificado

fonte
6
Fora do assunto. Pertence a stackoverflow.com.
usar o seguinte comando
Para mim, uma abordagem de máquina de estado é muito menos arriscada do que lidar com ponteiros de função. Seu byte de entrada é passado para uma estrutura de máquina de estado (pode ser tão simples quanto um caso de switch) que salta para vários bits de código em função da variável ou variáveis ​​de estado. Além disso, você não sabe ao certo de onde vem o seu stdin (não que isso realmente importe muito, mas estou curioso).
Adam Lawrence
9
@EJP Eu discordo. Só porque existe uma sobreposição, não significa que ele tenha que estar em um site ou no outro. Ele está fazendo perguntas relacionadas ao design e programação de sistemas embarcados de baixo nível, que parecem sobre os tópicos em qualquer um dos lugares.
precisa saber é o seguinte
Máquinas de estado podem ser baseadas na indireção de função: a chamada para a função de transição de estado retorna uma nova função de transição de estado.
Kaz
11
Vamos ter essa discussão aqui .

Respostas:

21

Uma função

int f(int x){ .. }

Obter o endereço de uma função (e armazená-lo em uma variável)

int (*fp)(int) = f;

Chamar uma função em um endereço especificado

int x = (*fp)( 12 );
Wouter van Ooijen
fonte
3
+1, óbvio no mundo C e ASM, mas não tão óbvio para quem começou com idiomas OO.
Anindo Ghosh
4
A chamada fp também pode ser escrita como 'int x = fp (12);' para aumentar a legibilidade.
precisa saber é o seguinte
11
Eu tenho que admitir, trabalhando principalmente em C # e Java atualmente, às vezes sinto falta daqueles bons e antigos indicadores de função do C. Eles são perigosos quando não são executados exatamente da maneira certa, e a maioria das tarefas pode ser realizada de outras maneiras, mas com certeza são úteis naquelas poucas vezes em que são realmente úteis.
um CVn 25/01
@ MichaelKjörling: Tão verdadeiro. Estou constantemente alternando entre programação C e C # incorporada (mesmo implementando código semelhante para comunicação entre o dispositivo e o PC) e, por mais poderoso que seja o C #, ainda gosto muito de C.
Rev1.0
2
fp(12)não aumenta a legibilidade (*fp)(12). Eles têm legibilidade diferente. (*fp)(12)lembra o leitor que fpé um ponteiro e também se assemelha à declaração de fp. Na minha opinião, esse estilo está associado a fontes antigas, como X11 internals e K&R C. Um possível motivo para associar-se ao K&R C é porque, no ANSI C, é possível declarar fpusando a sintaxe da função, se fpfor um parâmetro: int func(int fp(double)) {}. Em K&R C isso seria int func(fp) int (*fp)(double); { ... }.
Kaz
17

Embora a resposta da Wouters esteja absolutamente correta, talvez isso seja mais amigável para iniciantes e um exemplo melhor em relação à sua pergunta.

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

Se não for óbvio que o ponteiro da função realmente tem um endereço de função de destino atribuído, é aconselhável executar uma verificação NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
Rev1.0
fonte
8
+1 para a verificação nula, mas observe que o compilador pode não garantir que o valor no endereço na RAM que fpocupe seja realmente NULL inicialmente. Eu faria int (*fp)(int) = NULL;como uma declaração e inicialização combinadas para ter certeza - você não deseja pular para algum local de memória aleatória por causa de algum valor bobo que simplesmente estava lá. Então apenas atribua fpcomo em sua Example()função.
um CVn 25/01
11
Obrigado pelo comentário, eu adicionei a inicialização NULL.
Rev1.0
4
@ MichaelKjörling bom ponto, mas se fpfor uma "variável global" (como no código acima), é garantido que ela será inicializada NULL(sem ter que fazê-lo explicitamente).
Alok