Функционален указател с GCC, присвояване на адрес

Сблъсках се с нещо, което напълно не разбирам. Има прототип на функция:

typedef void ( * TMain ) ( void );

и функционална променлива:

TMain myFunc = MyFunc;
...
myFunc ();

Това работи добре, разбира се. Защо не трябва.

От MAP-файла знам, че "MyFunc" е на място 0x20100. И сега най-смешното. След присвояването "myFunc = MyFunc;" променливата "myFunc" не съдържа стойността 0x20100, а по-скоро 0x20101!

Проблемът ми е, че трябва да извикам функция, чийто адрес знам от таблица. Затова реших, че мога да го направя така

myFunc = ( TMain ) myTable [ 5 ];    // that would be 0x20100
myFunc ();                           // which produces a proper crash

Въпреки това, ако го направя

myFunc = ( TMain ) ( ( Int8 * ) myTable [ 5 ] + 1 );  
myFunc ();

тогава работи.

какво става тук? Винаги ли трябва да добавям отместване от 1 или това е повече или по-малко случайно? Или има по-добър (и работещ) начин за изпълнение на задачата?

Благодаря много за всякакви съвети. Уолтър


person Walter    schedule 01.02.2012    source източник
comment
Не съм сигурен, но това може да зависи от архитектурата. Така че може да искате да кажете коя е целевата архитектура.   -  person phimuemue    schedule 01.02.2012
comment
Масивът myTable от тип TMain[] ли е? Предполагам, че не е, тъй като го хвърляте.   -  person Blagovest Buyukliev    schedule 01.02.2012
comment
Някои въпроси към вас: Каква платформа използвате (ARM, x86...)? Проверявали ли сте някога действителния адрес на функцията с дебъгер? Можете ли да видите как компилаторът генерира непрякото извикване myFunc()?   -  person Giuseppe Guerrini    schedule 01.02.2012
comment
защо съхранявате адресите, а не функциите? вместо да декларирате int myTable[N], можете да направите TMain myTable[N] и по-късно нямате нужда от преобразуване: myfunc = myTable[5], или извиквате директно mytable[5](), дори ако преобразувате функциите, за да попълните масива, ще работи правилно.   -  person Bort    schedule 01.02.2012
comment
Or-ing в 1 може да е по-подходящо за задаване на режим Thumb тук.   -  person Michael Dorgan    schedule 11.04.2013


Отговори (2)


Предполагам, че сте на ARM цел и сте изградили програмата си в режим Thumb? (Палецът е по подразбиране на ARM Ubuntu или Linaro.)

Долният бит на адреса на функцията казва на процесора в кой набор от инструкции трябва да интерпретира функцията. 0 е режим ARM. 1 е режим на палец. По този начин всички указатели на функции в режим Thumb ще бъдат нечетни.

Други архитектури също използват този идиом по един или друг начин. Обикновено е безопасно просто да нулирате долните два бита на адреса (като го направите 4-байтов подравнен) и да приемете, че това е истинското местоположение на функцията.

person ams    schedule 01.02.2012

Няколко CPU архитектури запазват първите байтове на функция за конкретни цели. VAXen има маска за запис на регистър там. CDC Cyber ​​поставя адреса за връщане там. Някои използват някои битове от „адреса“, за да посочат адресна индиректност (направо не мога да си спомня кои, но са от 1970 г.).

Освен ако наистина не знаете какво правите, не трябва да пишете код, който прави този вид аритметика с указател. Присвоете на променливата името на функцията и свършете с нея: това гарантирано ще работи при всяка реализация на C.

person wallyk    schedule 01.02.2012