Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Как определить смещение переменной внутри структуры?  (Прочитано 22083 раз)
0 Пользователей и 8 Гостей смотрят эту тему.
sh_m
Гость
« : 18-08-2003 07:34 » 

Есть ли в Си какой-нибудь оператор, который выдает значение смещения переменной внутри структуры?

Например:
struct A
       {
       int p1;
       char b;
       int  p2;
       float val;
       char m;
       }

Как мне программно узнать, что val находится по смещению 9?
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #1 : 18-08-2003 07:46 » 

Он и по смещению 12 может быть из-за необходимости выравнивания у процессора.

Узнавай смещение вот так
 
int offset = (int)(&((struct A*)0)->val);
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #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
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 18-08-2003 08:02 » 

Вот и SlavaI меня опередил с ответом Улыбаюсь
Могу только порекомендовать для удобства и снижения вероятности ошибки сделать макрос:
Код:

#define member_offset)ptr,type,member:  ))int:)&))type*:0:->member::
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
SlavaI
Главный специалист

ru
Offline 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 » 

Спасибо за ответы! Только что проверил - работает!

Цитата: SlavaI
Он и по смещению 12 может быть из-за необходимости выравнивания у процессора.

Я с этим даже сталкивался. Проблема чаще всего всплывает при необходимости обмена данными по tcp между процессами, скомпилированными в разных операционках и соответственно разными средствами с разными опциями по-умолчанию.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 18-08-2003 08:32 » 

Цитата

Член ptr не нужен.

Поторопился немного... Ага
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #7 : 18-08-2003 08:44 » 

Цитата

Я с этим даже сталкивался. Проблема чаще всего всплывает при необходимости обмена данными по tcp между процессами, скомпилированными в разных операционках и соответственно разными средствами с разными опциями по-умолчанию.


Чтобы этого безобразия не было надо использовать pragma, у Майкрософтовского есть #pragma pack, чтобы исключить выравнивание.
Записан
sh_m
Гость
« Ответ #8 : 18-08-2003 09:00 » 

Цитата: SlavaI

Чтобы этого безобразия не было надо использовать pragma, у Майкрософтовского есть #pragma pack, чтобы исключить выравнивание.


У меня проблема была с Borland Builder-ом. Я подправил опцию компилятора (Project->Options...->Advansed Compiler->Data Alignment = Byte) и все стало нормально.
Но #pragma конечно покорректнее. Сейчас глянул Help, в Borland-e такая тоже есть.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 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
Главный специалист

ru
Offline Offline

« Ответ #11 : 27-08-2003 05:14 » 

Цитата

Стандартная библиотека имеет право разыменовывать null-указатель в своей реализации


Что то я не понял утверждения.
Ты что имеешь в виду вот это-
*((class A*)NULL)
так это ошибка, если память по нулевому адресу не выделена, а в большинстве ОС так сделано специально. Что-то я сомневаюсь в наличии в стандарте такого скользкого пункта. Какой номер у него?
Записан
Yess
Гость
« Ответ #12 : 27-08-2003 05:24 » 

Я говорю не про Стандарт, а про конкретную реализацию стандартной библиотеки под конкретную платформу. Т. е. для этой платформы точно известно, что в результате разыменования null-указателя получится 0x00000000. Но это может не выполнятся для других платформ.
Записан
SlavaI
Главный специалист

ru
Offline 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' это условие выполняется.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines