Разъяснение указателей функций в C

Следующий код приведен из примера abo3.c из Insecure Программирование - см. Также Зачем прикладывать extern puts к указателю функции (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);
}

В частности, эта строка:

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

Я думаю, что void (*fn)(char*) звучит как лямбда, но я знаю, что это не так. Тогда, может быть, это только игра с круглыми скобками, где void *fn(char*) является объявлением функции, и эта функция ссылается на system? Но почему параметр (char* не имеет имени? Разрешено ли это?

Ответ 1

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

Эта строка берет адрес неверно объявленного символа system (должен быть int(const char*), не неявный int), отбрасывает его в тип fn и инициализирует эту новую локальную переменную.
Тип void(*)(char*) или указатель на функцию, получающую одиночный char* и ничего не возвращающий.


Ну, этот код - все виды плохого:

  • Традиционное присвоение первых двух аргументов main отменяется:

    int main(int argv,char **argc)
    
  • Объявление стандартных библиотек-функций system и puts с использованием "implicit int" в качестве int s. Это даже не неправильный тип функции, а тип данных!

    extern system,puts;
    
  • Листинг адреса символов для указателя функции почти нормальный. Теперь, если бы это был правильный тип... Во всяком случае, на ящике с указателями данных и указателями кода одинакового размера он, вероятно, не потеряет никакой информации.

    void (*fn)(char*)=(void(*)(char*))&system; // <==
    fn=(void(*)(char*))&puts;
    
  • Проверка того, что argv [!], по крайней мере, 2, действительно не должна быть опущена здесь:

    strcpy(buf,argc[1]);
    
  • Вызов функции с помощью указателя функции неправильного типа - UB. Не делай этого. Кроме того, проверяя, является ли argv [!] Не менее 3 до того, как эта точка не является необязательной. п (ARGC [2]);

  • Опираясь на неявное объявление для strcpy и exit также очень плохо. Ни один из прототипов не согласуется с этим! (они не возвращают int)

Ответ 2

Он объявляет переменную fn как указатель функции (к функции, которая имеет один аргумент типа char * и ничего не возвращает (void).

Эта переменная инициализируется адресом system - см. http://linux.die.net/man/3/system. Как отмечено на этой странице, для этого потребуется бросок, как указано

Ответ 3

Тогда, может быть, это только игра с круглыми скобками, где void * fn (char *) является объявлением функции, и эта функция ссылается на систему, я думаю,

void (*fn)(char *) не является функцией, это указатель на функцию. Вы можете ознакомиться здесь, чтобы узнать о указателях функций.. И () имеют значение здесь, вы не можете игнорировать их.

Но почему параметр (char *) не имеет имени? это разрешено?

Да, это разрешено. Его имя не так важно, но его тип есть.

Ответ 4

Это чистый и простой указатель на функцию, которому присваивается адрес вызова system

Ответ 5

Сначала вы столкнулись с проблемой объявления указателя на system.

Затем вы выбросите все это, переопределив его, чтобы указать на puts.

Затем, кажется, вы пытаетесь индексировать int, но вы изменили обычное соглашение об именах для main, которое

int main (int argc, char **argv)

Ответ 6

Это указатель на функцию. Позвольте мне показать вам, если вы попытаетесь сделать приложение на C, которое будет использовать текстовые меню, где вместо switch я буду использовать указатель на функцию:

#include <stdio.h>
#include<unistd.h>

void clearScreen(int x);
int exitMenu(void);
int mainMenu(void);
int updateSystem(void);
int installVlcFromPpa(void);
int installVlcFromSource(void);
int uninstallVLC(void);
int chooseOption(int min, int max);
void showMenu(const char *question, const char **options, int (**actions)(void), int length);
int installVLC(void);
int meniuVLC(void);
void startMenu(void);

int main(void){
    startMenu();
    return 0;
}

void clearScreen(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(int min, 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), 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};

    showMenu(question, options, actions, 3);
    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};

    showMenu(question, options, actions, 3);

    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};

    showMenu(question, options, actions, 3);
}

Скомпилируйте его и попробуйте.

Ответ 7

Строка объявляет переменную указателя функции, которая принимает параметр и ничего не возвращает (void), это необходимо вместе с приведением типа к тому же прототипу функции, поскольку функции extern похожи по своему типу на указатель void * они не ранние сведения во время компиляции.