Разъяснение указателей функций в 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 * они не ранние сведения во время компиляции.