sh_m
Гость
|
|
« : 18-08-2003 07:34 » |
|
Есть ли в Си какой-нибудь оператор, который выдает значение смещения переменной внутри структуры?
Например: struct A { int p1; char b; int p2; float val; char m; }
Как мне программно узнать, что val находится по смещению 9?
|
|
|
Записан
|
|
|
|
SlavaI
Главный специалист
Offline
|
|
« Ответ #1 : 18-08-2003 07:46 » |
|
Он и по смещению 12 может быть из-за необходимости выравнивания у процессора.
Узнавай смещение вот так int offset = (int)(&((struct A*)0)->val);
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #2 : 18-08-2003 07:55 » |
|
Вот примерчик из исходников linux-а : /* linux/list.h */
#define list_entry)ptr,type,member: ))type*:))char*:)ptr:-)unsigned long:)&))type*:0:->member:::
По адресу (ptr) члена структуры (member) и ее типу (type) находит адрес начала структуры. Поняв как это делается, нетрудно сделать и обратное вычисление.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
RXL
|
|
« Ответ #3 : 18-08-2003 08:02 » |
|
Вот и SlavaI меня опередил с ответом Могу только порекомендовать для удобства и снижения вероятности ошибки сделать макрос: #define member_offset)ptr,type,member: ))int:)&))type*:0:->member::
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
SlavaI
Главный специалист
Offline
|
|
« Ответ #4 : 18-08-2003 08:08 » |
|
Токо так сделай макрос #define member_offset)type,member: ))int:)&))type*:0:->member::
Член ptr не нужен. А у нас в винде тоже похожий макрос есть // begin_winnt begin_ntminiport // // Calculate the byte offset of a field in a structure of type type. //
#define FIELD_OFFSET)type, field: ))LONG:)LONG_PTR:&)))type *:0:->field::
// // Calculate the address of the base of the structure given its type, and an // address of a field within the structure. //
#define CONTAINING_RECORD)address, type, field: ))type *:) \ )PCHAR:)address: - \ )ULONG_PTR:)&))type *:0:->field:::
|
|
|
Записан
|
|
|
|
sh_m
Гость
|
|
« Ответ #5 : 18-08-2003 08:26 » |
|
Спасибо за ответы! Только что проверил - работает! Он и по смещению 12 может быть из-за необходимости выравнивания у процессора.
Я с этим даже сталкивался. Проблема чаще всего всплывает при необходимости обмена данными по tcp между процессами, скомпилированными в разных операционках и соответственно разными средствами с разными опциями по-умолчанию.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #6 : 18-08-2003 08:32 » |
|
Член ptr не нужен.
Поторопился немного...
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
SlavaI
Главный специалист
Offline
|
|
« Ответ #7 : 18-08-2003 08:44 » |
|
Я с этим даже сталкивался. Проблема чаще всего всплывает при необходимости обмена данными по tcp между процессами, скомпилированными в разных операционках и соответственно разными средствами с разными опциями по-умолчанию.
Чтобы этого безобразия не было надо использовать pragma, у Майкрософтовского есть #pragma pack, чтобы исключить выравнивание.
|
|
|
Записан
|
|
|
|
sh_m
Гость
|
|
« Ответ #8 : 18-08-2003 09:00 » |
|
Чтобы этого безобразия не было надо использовать pragma, у Майкрософтовского есть #pragma pack, чтобы исключить выравнивание.
У меня проблема была с Borland Builder-ом. Я подправил опцию компилятора (Project->Options...->Advansed Compiler->Data Alignment = Byte) и все стало нормально. Но #pragma конечно покорректнее. Сейчас глянул Help, в Borland-e такая тоже есть.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Online
Сообщений: 13
|
|
« Ответ #9 : 26-08-2003 15:23 » |
|
я не до конца понимаю вот что: Узнавай смещение вот так int offset = (int)(&((struct A*)0)->val);
:arrow: что возвращает выражение (struct A*)0 :arrow: будет ли строке int offset = (int)(&((struct A*)0)->val) эквивалентно следующее: A a1; int offset = )int:)&)a1->val:: - )int:)&a1:
|
|
|
Записан
|
|
|
|
Yess
Гость
|
|
« Ответ #10 : 27-08-2003 03:26 » |
|
В страндартной библиотеке есть макрос offsetof (stddef.h), который определен как #define offsetof(s,m) (size_t)&(((s *)0)->m). Теперь по шагам:
1. ((s *)0) - разыменование null-указателя. 2. &(((s *)0)->m) - здесь будет получен адрес члена m 3. (size_t)&(((s *)0)->m) - приведение адреса к size_t. Это эквиваленто следующему
struct S { int a; int b; int c; };
int main() { S* p = (S*)0; // здесь p==0x00000000 size_t* b =(size_t*)&(p->b); // здесь b==0x00000004. // После преобразования к (size_t) получаем 4, - это и есть смещение b внутри S
return 0; }
И замечания: 1. Стандартная библиотека имеет право разыменовывать null-указатель в своей реализации. Ты сам в своей программе - нет, т.к. это не является легальным С++.
2. offsetof может применяться только для POD-типов.
|
|
|
Записан
|
|
|
|
SlavaI
Главный специалист
Offline
|
|
« Ответ #11 : 27-08-2003 05:14 » |
|
Стандартная библиотека имеет право разыменовывать null-указатель в своей реализации
Что то я не понял утверждения. Ты что имеешь в виду вот это- *((class A*)NULL) так это ошибка, если память по нулевому адресу не выделена, а в большинстве ОС так сделано специально. Что-то я сомневаюсь в наличии в стандарте такого скользкого пункта. Какой номер у него?
|
|
|
Записан
|
|
|
|
Yess
Гость
|
|
« Ответ #12 : 27-08-2003 05:24 » |
|
Я говорю не про Стандарт, а про конкретную реализацию стандартной библиотеки под конкретную платформу. Т. е. для этой платформы точно известно, что в результате разыменования null-указателя получится 0x00000000. Но это может не выполнятся для других платформ.
|
|
|
Записан
|
|
|
|
SlavaI
Главный специалист
Offline
|
|
« Ответ #13 : 27-08-2003 06:03 » |
|
Т. е. для этой платформы точно известно, что в результате разыменования null-указателя получится 0x00000000.
А что за платформа такая? Какая-то неестественная, стандартно разработчики операционок память по нулевому адресу не выделяют, специально чтобы ловить ошибки типа разименования нулевого указателя.
|
|
|
Записан
|
|
|
|
Yess
Гость
|
|
« Ответ #14 : 27-08-2003 06:13 » |
|
1. null-pointer гарантированно не является указателем ни на какой объект(пунк Стандарта сказать не могу - под рукой нет).
2. При обращении к экземпляру объекта, полученного в результате разыменования null-указателя возникает undefined behavior (точно не помню, вроде пункт 1.9 и 8.3.2)
3.Реализация стандартной библиотеки имеет право опираться на любые implementation defined особенности платформы, в том числе на определенность "неопределенного поведения". При этом, разумеется, требуется, чтобы поведение оставалось определенным для всех легальных применений данной конструкции. Для вышеприведенной реализации 'offsetof' это условие выполняется.
|
|
|
Записан
|
|
|
|
|