Diseño de codificación C - punteros de función?

24

Tengo un PIC18F46K22 y lo programo con el compilador XC8. Al final, tendré un sistema como una PC con stdiny stdout. Entonces, en el bucle principal, habrá una función que verificará si hay una nueva entrada. Si hay entrada, se llamará a una función en consecuencia. Así, por ejemplo cuando una A en la entrada stdin, el PIC se ejecutará una función como function_Alugar de function_Blo que se llama cuando una entrada de B.

Cuando el PIC termina con la función, quiero que la nueva entrada se envíe a la función. Entonces, al presionar A se abre el transmisor RS232, desde ese momento cada entrada se enviará a través de RS232. Al final, el proyecto es un editor de texto independiente. Entonces, al presionar A se abre el sistema de archivos, desde ese momento ya no está editando texto, sino mirando a través de una lista de archivos. Eso significa que presionar Arriba y Abajo significa algo diferente que en el entorno de edición de texto.

He pensado mucho sobre cómo programar esto en C. Lo pensé anoche y me gustaría saber si es posible y, de ser así, cómo. Lo que quiero hacer es:

  • La mainfunción llama a una función comofunction_A
  • function_Acambia una variable global function_addral puntero de dirección de la funciónin_function_A
  • A partir de ese momento, mainllama a la función function_addrcuando hay una nueva entrada.

Entonces, lo que necesitaría es una mainfunción que verifique si function_addres cero. Si es así, se debe llamar a una función 'normal', como function_A. Si no es así, se function_addrdebe llamar a la función at . También necesito un function_Aque cambie el function_addrpuntero a in_function_A.

Nota: cuando la función del sistema de archivos debe cerrarse, is_function_Asolo debe cambiar function_addra 0.

Básicamente, mi pregunta es cómo puedo

  • Obtener la dirección de una función (y almacenarla en una variable)
  • Llamar a una función a una dirección especificada

fuente
66
Fuera de contexto. Pertenece a stackoverflow.com.
user207421
Para mí, un enfoque de máquina de estado es mucho menos arriesgado que tratar con punteros de función. Su byte de entrada se pasa a una estructura de máquina de estado (podría ser tan simple como una caja de interruptor) que salta a varios bits de código en función de la variable o variables de estado. Además, no tiene totalmente claro de dónde viene su stdin (no es que realmente importe mucho, pero tengo curiosidad).
Adam Lawrence el
99
@EJP No estoy de acuerdo. El hecho de que haya una superposición no significa que tenga que estar en un sitio u otro. Está haciendo preguntas relacionadas con el diseño y la programación de sistemas integrados de bajo nivel, que parece ser el tema en cualquier lugar.
Kortuk
Las máquinas de estado se pueden basar en la función indirecta: la llamada a la función de transición de estado devuelve una nueva función de transición de estado.
Kaz
1
Vamos a tener esta discusión aquí .

Respuestas:

21

Una función

int f(int x){ .. }

Obtener la dirección de una función (y almacenarla en una variable)

int (*fp)(int) = f;

Llamar a una función a una dirección especificada

int x = (*fp)( 12 );
Wouter van Ooijen
fuente
3
+1, obvio en el mundo C y ASM, pero no tan obvio para aquellos que comenzaron con lenguajes OO.
Anindo Ghosh
44
La llamada fp también se puede escribir como 'int x = fp (12);' para aumentar la legibilidad
Rev1.0
1
Tengo que admitir que, trabajando principalmente en C # y Java actualmente, a veces extraño esos buenos punteros de función de C. Son peligrosos cuando no se hacen exactamente bien, y la mayoría de las tareas se pueden realizar de otras maneras, pero seguramente son útiles las pocas veces que realmente son útiles.
un CVn el
@ MichaelKjörling: Muy cierto. Estoy cambiando constantemente entre la programación integrada C y C # (incluso implementando un código similar para la comunicación entre el dispositivo y la PC) y, por muy poderoso que sea C #, todavía disfruto mucho de C.
Rev1.0
2
fp(12)no aumenta la legibilidad por encima (*fp)(12). Tienen diferente legibilidad. (*fp)(12)recuerda al lector que fpes un puntero, y también se asemeja a la declaración de fp. En mi opinión, este estilo está asociado con fuentes antiguas, como elementos internos X11 y K&R C. Una posible razón por la que podría estar asociado con K&R C es porque en ANSI C, es posible declarar fpusando la sintaxis de la función, si fpes un parámetro: int func(int fp(double)) {}. En K&R C eso hubiera sido int func(fp) int (*fp)(double); { ... }.
Kaz
17

Si bien la respuesta de Wouters es absolutamente correcta, esto puede ser más amigable para los principiantes y un mejor ejemplo con respecto a su pregunta.

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 */
}

Si no es obvio que el puntero de función realmente tiene asignada una dirección de función de destino, es aconsejable realizar una comprobación NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
Rev1.0
fuente
8
+1 para la verificación nula, pero tenga en cuenta que el compilador podría no garantizar que el valor en la dirección en la RAM que fpocupa es en realidad inicialmente NULL. Lo haría int (*fp)(int) = NULL;como una declaración e inicialización combinadas para estar seguro: no desea saltar a una ubicación de memoria aleatoria debido a un valor tonto que acaba de estar allí. Luego solo asigna fpcomo en tu Example()función.
un CVn el
1
Gracias por el comentario, agregué la inicialización NULL.
Rev1.0
44
@ MichaelKjörling buen punto, pero si fpes una "variable global" (como en el código anterior), entonces se garantiza que se inicializará NULL(sin tener que hacerlo explícitamente).
Alok