LINUX.ORG.RU

Указатель на указатель на функцию

 


3

4

Небольшой и, возможно, глупый вопрос по Си.

void f(void (**some_func)(void*)) {
    (*some_func)(some_func);
}
...
struct {
    void (*func_ptr)(void*);
    ...
} some_struct = { ... };
f((void (**)(void*))&some_struct);

Является ли приведённый выше код корректным?

По сути дела он базируется на двух предположениях:

1) Адрес первого поля структуры совпадает с адресом структуры. Пустые байты для выравнивания (если компилятор решил их добавить) всегда добавляются после поля, но не перед ним.

2) Указатель на указатель на функцию это самый обычный указатель на данные. Указатель на функцию сам по себе может быть особенным. Например, если речь идёт о гарвардской архитектуре, в которой код и данные имеют разные адресные пространства. Соответственно, указатель на функцию может быть чем-то принципиально иным, нежели указатель на данные (даже банально разрядности адресов в памяти программ и памяти данных могут быть разными). Но указатель на указатель на функцию это уже самое обычное число.

Верны ли эти предположения для всех архитектур, где имеет место быть Си?

★★★★★

Много букв :-)

Указатель на функцию сам по себе может быть особенным. Например, если речь идёт о гарвардской архитектуре

C потому и придуман был, чтобы от архитектуры абстрагироваться :-)

Но указатель на указатель на функцию это уже самое обычное число.

Нет, указатель - это не число по определению :-) Указатель может быть представлен числом, но это, опять же, зависит от архитектуры :-) Код с явными преобразованиями указателей в числа нельзя назвать переносимым в общем случае (т.е. без вставок условных дефайнов) :-)

anonymous ()

По сути дела он базируется на двух предположениях:
1) Адрес первого поля структуры совпадает с адресом структуры.
Это не предположение, а факт, гарантируемый стандартом С.

akk ★★★★ ()
Ответ на: комментарий от akk

Это очень хорошо. А что про второе предположение? Указатель на указатель на функцию это то же самое, что и указатель на другие данные?

KivApple ★★★★★ ()
Ответ на: комментарий от KivApple

Указатель на указатель на функцию это то же самое, что и указатель на другие данные?

Это просто указатель. Указатель на функцию ничем не отличается от других указателей. Ты можешь сделать указатель «вникуда» и попытаться использовать как указатель на функцию, ничего хорошего из этого не выйдет, но никто не запрещает тебе это сделать.

DELIRIUM ★★★★★ ()
Последнее исправление: DELIRIUM (всего исправлений: 1)
Ответ на: комментарий от DELIRIUM

Теоретически указатель на функцию может иметь другую разрядность, нежели указатель на данные, если у нас гарвадская архитектура. Соответственно преобразование может пройти с потерями. Соответственно вопрос всегда ли сам указатель считается данными, а также равноправны ли указатели на данные.

KivApple ★★★★★ ()
Ответ на: комментарий от KivApple

Теоретически указатель на функцию может иметь другую разрядность, нежели указатель на данные, если у нас гарвадская архитектура.

Только при чём тут стандартный C? :-) Лол :-)

anonymous ()
Ответ на: комментарий от anonymous

При том, что стандарт вроде как предусматривает всякие необычные случаи (даже char может быть не 8-битный). Соответственно, хотелось бы узнать гарантирует ли он, что указатели являются простыми данными, а указатели на данные равноправны (в смысле, что можно свободно приводить типы между собой - если не обращаться к промежуточным значениям и в итоге привести всё к исходному типу, то потери данных не случится).

KivApple ★★★★★ ()
Ответ на: комментарий от KivApple

Да.

На 39 странице говорится, что все типы делятся на object types и function types.

То, что указатель - это данные, написано на странице 42 стандарта:

A pointer type is a complete object type.

На 56 странице сказано, что любой указатель на данные может быть без потерь конвертирован в любой другой указатель на данные, с учётом выравнивания.

Точно также любой указатель на функцию одного типа может быть без потерь конвертирован в указатель на функцию другого типа.

Указатель любого типа может быть конвертирован в целое число и если он не NULL, то результат implementation-defined. То же самое при конвертировании числа в указатель.

anonymous ()

Ты задаешь вопросы про стандарт, а размышляешь про компиляторы и архитектуры. Не надо так.

anonymous ()
Ответ на: комментарий от anonymous

Большое спасибо. Это то, что мне было нужно знать.

KivApple ★★★★★ ()
Ответ на: комментарий от anonymous

Код с явными преобразованиями указателей в числа нельзя назвать переносимым в общем случае

{,u}intptr_t?

hateyoufeel ★★★★★ ()
Ответ на: комментарий от DELIRIUM

Ты можешь сделать указатель «вникуда» и попытаться использовать как указатель на функцию, ничего хорошего из этого не выйдет

Я это использовал для ребута атмелов: ((void(*)(void))0)(); Работало на ура. ;)

beastie ★★★★★ ()
Ответ на: комментарий от hateyoufeel

{,u}intptr_t?

«These types are optional.» :-) Поэтому,

Код с явными преобразованиями указателей в числа нельзя назвать переносимым в общем случае

anonymous ()
Ответ на: комментарий от beastie

Я это использовал для ребута атмелов: ((void(*)(void))0)(); Работало на ура. ;)

Senior developer detected! =)

DELIRIUM ★★★★★ ()
Ответ на: комментарий от DELIRIUM

Между прочим абсолютно легально. Так что не надо.

beastie ★★★★★ ()
Ответ на: комментарий от beastie
((void(*)(void))0)();

Растолкуй, что сие значит. Из какой-то фигни делается указатель на функцию и на нее передается управление? А кто гарантирует, что всегда будет передан осмысленный адрес?!

ЯННП.

Twissel ★★★★★ ()
Ответ на: комментарий от Twissel

Вкреце: JMP 0x0000 — в atmel entry point находится по нулевому адрессу и мы живём в real mode без сегментов.

i386 (в DOS времена) можно было перегрузить прыгунув на 0xFFFF.

beastie ★★★★★ ()
Ответ на: комментарий от Twissel

int19h — это только один из 3-х способов. ;)

  1. Bootstrap loader
    .model small
    .code
    	org 100h
    start:
    	int 19h			; Bootstrap loader
    end start
    
  2. Jump to Power On Self Test
    .model small
    .code
    	org 100h
    start:
    	db 0EAh                 ; Jump to Power On Self Test - Cold Boot
    	dw 0,0FFFFh
    end start
    
  3. Keyboard reset
    .model small
    .code
    	org 100h
    start:
    	mov ah,0Dh
    	int 21h                 ; DOS Services  ah=function 0Dh
    				;  flush disk buffers to disk
    	sti                     ; Enable interrupts
    	hlt                     ; Halt processor
    	mov al,0FEh
    	out 64h,al              ; port 64h, kybd cntrlr functn
    				;  al = 0FEh, pulse CPU reset
    end start
    
beastie ★★★★★ ()
Ответ на: комментарий от beastie

Про третий метод не знал, интересно.

Это я к тому, что линукс с людьми делает, я уже напрочь забыл, что когда-то была таблица векторов прерываний и прыгай себе до посинения.

Twissel ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.