Равенство указателей функций в C
мои вопросы:
- равенство указателя функции гарантировано стандартом C?
- если ответ (1) - да. Это так, независимо от указателя, получаемого в разных конечных единицах компиляции (например, основной исполняемый файл и общая библиотека)?
- как динамический загрузчик справляется с этим? (Я могу подумать о нескольких причинах, по которым это может быть сложно, все связано с кодом PIC (например, GOT tables in elf и какой бы эквивалент КОФФ ни использовал для этого)). Независимо от (1) и (2) загрузчик linux, похоже, гарантирует это.
вот пример. вопросы выше сводятся к тому, гарантирует ли C то, что main.c
принты: "Function equality: 1"
или "Function equality: 0"
и в первом случае, как динамический загрузчик это сделать.
common.h:
extern void * getc_main;
extern void * getc_shared;
void assign_getc_shared();
main.c:
#include <stdio.h>
#include "common.h"
int main()
{
getc_main = (void*) getc;
assign_getc_shared();
printf("Function equality: %dn", getc_main == getc_shared);
return 0;
}
shared.c:
#include <stdio.h>
#include "common.h"
void assign_getc_shared()
{
getc_shared = (void*) getc;
}
в Unix это будет скомпилирован со следующими командами:
cc -shared -fPIC -o libshared.so shared.c
cc -o main main.c -L. -lshared
и:
LD_LIBRARY_PATH=. ./main
1 ответов
C 2011 (проект Комитета N1570) 6.5.9 6: "два указателя сравниваются равными тогда и только тогда, когда ... оба являются указателями на одну и ту же ... функцию .... Так что да, два указателя на одну и ту же функцию равны.
когда адрес функции берется в двух разных объектных модулях, компилятор помещает заполнитель в объектный код. Этот заполнитель заполняется, когда объектные модули связаны в исполняемый файл или связаны с динамической библиотекой во время выполнения.
для динамического библиотеки, либо динамический загрузчик заполняет все заполнители в исполняемом файле по мере необходимости, либо адрес каждой функции фактически является местоположением некоторого кода заглушки, который переходит к фактической функции, а заполнитель или используемый этим кодом заглушки заполняется динамическим загрузчиком.
кроме того, обратите внимание, что исполняемый файл может содержать более одного экземпляра функции. Компилятор может вставлять функцию inline в нескольких местах или может по своим собственным причинам включать специализация функции, а также общий вариант. Однако, когда адрес функции берется, компилятор должен предоставить адрес одной общей версии. (Или компилятор должен убедиться, что программа ведет себя так, как если бы это было сделано. Например, если компилятор может обнаружить, что программа не сравнивает указатели, то теоретически она может использовать другой адрес для некоторых экземпляров адреса функции.)