Aclaración sobre los punteros de función en C

El siguiente código proviene del ejemplo abo3.c de Progtwigción insegura . Consulte también ¿Por qué convertir las extern puts a un puntero de función (void(*)(char*))&puts ? :

 int main(int argv,char **argc) { extern system,puts; void (*fn)(char*)=(void(*)(char*))&system; // <== char buf[256]; fn=(void(*)(char*))&puts; strcpy(buf,argc[1]); fn(argc[2]); exit(1); } 

Específicamente esta línea:

 void (*fn)(char*)=(void(*)(char*))&system; 

Creo que void (*fn)(char*) suena como un lambda, pero sé que no lo es. Entonces, tal vez esto sea solo un juego con paréntesis, donde void *fn(char*) es una statement de una función y esta función hace referencia al system . Pero, ¿por qué el parámetro (char* ) no tiene nombre? ¿Está esto permitido?

 void (*fn)(char*)=(void(*)(char*))&system; 

Esa línea toma la dirección del system símbolos mal declarado (debe ser int(const char*) , no implícito int ), la convierte al tipo de fn e inicializa esa nueva variable local.
El tipo es void(*)(char*) o puntero para funcionar recibiendo un solo char* y no devolviendo nada.


Bueno, ese código es todo tipo de mal :

  1. La denominación tradicional de los dos primeros argumentos a main se invierte:

     int main(int argv,char **argc) 
  2. Declarar el system funciones de biblioteca estándar y puts usando “int implícito” como int s. ¡Ni siquiera es el tipo de función incorrecto, sino un tipo de datos!

     extern system,puts; 
  3. Convertir la dirección de los símbolos en un puntero de función es casi sensato. Ahora, si solo fuera del tipo correcto … De todos modos, en un cuadro con punteros de datos e indicadores de códigos del mismo tamaño, probablemente no se pierda ninguna información.

     void (*fn)(char*)=(void(*)(char*))&system; // <== fn=(void(*)(char*))&puts; 
  4. La verificación de si argv [!] Es al menos 2 no debería omitirse aquí:

     strcpy(buf,argc[1]); 
  5. Llamar una función a través de un puntero de función de tipo incorrecto es UB. No hagas eso Además, verificar si argv [!] Es al menos 3 antes de este punto no es opcional. fn (argc [2]);

  6. Confiar en la statement implícita para strcpy y exit también es realmente malo. ¡Ninguno tiene un prototipo consistente con eso! (no vuelven int )

Declara la variable fn como un puntero de función (a una función que tiene un argumento de tipo char * y no devuelve nada ( void ).

Esta variable se inicializa con la dirección del system ; consulte http://linux.die.net/man/3/system . Como se indica en esta página, esto requerirá el reparto como se indica.

Entonces, quizás esto sea solo un juego con paréntesis, donde void * fn (char *) es una statement de una función y esta función hace referencia al sistema, creo

void (*fn)(char *) no es una función, es un puntero a función. Puede consultar aquí para aprender sobre punteros de función. . Y () importa aquí, no puedes ignorarlos.

¿Pero por qué el parámetro (char *) no tiene nombre? esto es permitido?

Sí, está permitido hacerlo. Su nombre no es tan importante pero su tipo es.

Es puro y simple puntero a la función, que se asigna la dirección a la llamada del system

Primero te metes en la molestia de declarar un puntero de función al system .

Luego lo tiras todo al redefinirlo para que apunte a puts .

Entonces parece que intenta indexar un int , pero ha invertido la convención de nomenclatura habitual para main , que es

 int main (int argc, char **argv) 

La línea está declarando una variable de un puntero de función que toma un parámetro y no devuelve nada (vacío), esto es necesario junto con el encasillado al mismo prototipo de función, ya que las funciones externas son similares en su tipo a un puntero void *, no son temprano encuadernado durante la comstackción

Es un puntero a la función. Déjame mostrarte si intentas crear una aplicación en C que use menús de texto donde en lugar de cambiar usaré el puntero a Función:

 #include  #include void clearScreen( const int x ); int exitMenu( void ); int mainMenu( void ); int updateSystem( void ); int installVlcFromPpa( void ); int installVlcFromSource( void ); int uninstallVLC( void ); int chooseOption( const int min, const int max ); void showMenu( const char *question, const char **options, int (**actions)( void ), const int length ); int installVLC( void ); int meniuVLC( void ); void startMenu( void ); int main( void ){ startMenu(); return 0; } void clearScreen( const int x ){ int i = 0; for( ; i < x ; i++ ){ printf( "\n" ); } } int exitMenu( void ) { clearScreen( 100 ); printf( "Exiting... Goodbye\n" ); sleep( 1 ); return 0; } int mainMenu( void ){ clearScreen( 100 ); printf( "\t\t\tMain Manu\n" ); return 0; } int updateSystem( void ) { clearScreen( 100 ); printf( "System update...\n" ); sleep( 1 ); return 1; } int installVlcFromPpa( void ) { clearScreen( 100 ); printf("Install VLC from PPA \n"); sleep( 1 ); return 0; } int installVlcFromSource( void ) { clearScreen( 100 ); printf( "Install VLC from Source \n" ); sleep( 1 ); return 0; } int uninstallVLC( void ) { clearScreen( 100 ); printf( "Uninstall VLC... \n" ); sleep( 1 ); return 1; } int chooseOption( const int min, const int max ){ int option,check; char c; do{ printf( "Choose an Option:\t" ); if( scanf( "%d%c", &option, &c ) == 0 || c != '\n' ){ while( ( check = getchar() ) != 0 && check != '\n' ); printf( "\tThe option has to be between %d and %d\n\n", min, max ); }else if( option < min || option > max ){ printf( "\tThe option has to be between %d and %d\n\n", min, max ); }else{ break; } }while( 1 ); return option; } void showMenu( const char *question, const char **options, int ( **actions )( void ), const int length) { int choose = 0; int repeat = 1; int i; int ( *act )( void ); do { printf( "\n\t %s \n", question ); for( i = 0 ; i < length ; i++ ) { printf( "%d. %s\n", (i+1), options[i] ); } choose = chooseOption( 1,length ); printf( " \n" ); act = actions[ choose - 1 ]; repeat = act(); if( choose == 3 ){ repeat = 0; } }while( repeat == 1 ); } int installVLC( void ) { clearScreen( 100 ); const char *question = "Installing VLC from:"; const char *options[10] = { "PPA", "Source", "Back to VLC menu" }; int ( *actions[] )( void ) = { installVlcFromPpa, installVlcFromSource, mainMenu }; size_t len = sizeof(actions) / sizeof (actions[0]); showMenu( question, options, actions, (int)len ); return 1; } int meniuVLC( void ) { clearScreen( 100 ); const char *question = "VLC Options"; const char *options[10] = { "Install VLC.", "Uninstall VLC.", "Back to Menu." }; int ( *actions[] )( void ) = { installVLC, uninstallVLC, mainMenu }; size_t len = sizeof(actions) / sizeof (actions[0]); showMenu( question, options, actions, (int)len ); return 1; } void startMenu( void ){ clearScreen( 100 ); const char *question = "Choose a Menu:"; const char *options[10] = { "Update system.", "Install VLC", "Quit" }; int ( *actions[] )( void ) = { updateSystem, meniuVLC, exitMenu }; size_t len = sizeof(actions) / sizeof (actions[0]); showMenu( question, options, actions, (int)len ); } 

Compílalo y pruébalo.