Макрос с приведением типов в C, Детальное изучение offsetof
Следующий код и его вывод:
#include <stdio.h>
int x = 0;
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)x)->MEMBER)
#define offsetof_1(TYPE, MEMBER) ((size_t) &((TYPE *)1)->MEMBER)
#define offsetof_2(TYPE, MEMBER) ((size_t) &((TYPE *)2)->MEMBER)
#define offsetof_3(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
struct test{};
struct m {
int b;
char w;
struct test t;
int c;
};
int main(void) {
printf("Checking:%x\n",offsetof(struct m,b));
printf("Checking:%x\n",offsetof_1(struct m,b));
printf("Checking:%x\n",offsetof_2(struct m,b));
printf("Checking:%x\n",offsetof_3(struct m,b));
return 0;
}
Output:
Checking:0
Checking:1
Checking:2
Checking:0
Я пытаюсь понять используемое здесь типирование. Я думаю, как компилятор это лечит (type *)(value)
как этот тип, начиная с адреса (значения). Следовательно, для данной структуры ожидаемое значение равно 0, т.е. offsetof равно b, но, поскольку мы использовали разные значения для приведения типов, мы получаем другое смещение. Пожалуйста, дайте мне знать, если мое понимание верно. Я просто пытаюсь понять, как литералы типизированы и каковы их последствия. Это используется в ядре Linux. Отсюда и их теги.
1 ответ
offsetof
это стандартное средство. Стандарт C определяет его поведение определенным образом, но не определяет, как его реализует компилятор. Большинство компиляторов полагаются на неопределенное поведение, определяя особый случай ради offsetof
реализация.
То, что вы сделали, - чепуха с точки зрения языковой семантики. Лечить ценность 2
в качестве указателя это классический пример неопределенного поведения.
Это не проектная цель языка, чтобы пользователь мог определять свои собственные offsetof
так что лучше не пытаться.
"Под капотом" указатели yes обычно являются скалярами, содержащимися в машинных регистрах, и скалярная арифметика используется для получения адреса члена структуры. Выполнение той же арифметики для получения адреса объекта в "нулевом местоположении" дает его общее смещение внутри любого объекта. Использование целых чисел помимо нуля в качестве местоположений может привести к арифметическому результату, а может и нет. (Обычно, но идея бессмысленная.)
Экспериментирование с программами, которые являются концептуальной чепухой, представляет собой рискованный способ выяснить, как работает система, потому что вы рискуете, что она отметит ошибку или воспользуетесь ярлыком, обнаружив, что что-то не так.