LINUX.ORG.RU

[C] Преобразование указателей на структуры

 


0

0
#include <stdio.h>

struct s_base
{
  int tag;
};

struct s1
{
  int tag;
  int data;
};

struct s2
{
  int tag;
  float data;
};

int main()
{
  struct s1 a;
  struct s_base *b = (struct s_base *) &a;
  a.tag = 1234;
  if(a.tag != b->tag)
  {
    printf("can never happen\n");
  }
  return 0;
}

У трёх структур общее «начало». Указатели на экземпляры структур s1 и s2 преобразовываются к указателям s_base. Гарантируется ли стандартом Си, что условие внутри if() никогда не выполнится?


Мне вот интересно, какой индусский код вы планируете создать на основе полученной информации.

dn2010 ★★★★★
()

не гарантируется. Вообще тут сразу имеет место быть варнинг в строчке

struct s_base *b = (struct s_base *) &a;

warning: dereferencing pointer ‘b’ does break strict-aliasing rules

в стандарте такой случай упоминается как UB.

попробуйте, например:

gcc -O3 -W -Wall your_buggy_program.c

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

Мне вот интересно, какой индусский код вы планируете создать на основе полученной информации.

Может свою реализацию API сокетов со всеми его sockaddr, sockaddr_in?

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

В XLib такая же ситуация, как и в описанном примере. XEvent и всякие XUnmapEvent, XGraphicsExposeEvent и т.п. сделаны именно по этому принципу. И X Window вроде бы не индусы проектировали.

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

> В XLib такая же ситуация, как и в описанном примере.

не такая же. XEvent - это union

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

> warning: dereferencing pointer ‘b’ does break strict-aliasing rules

Насколько я знаю, это GCC-специфичное предупреждение, причем оно связано с особенностями оптимизатора - он предполагает, что на одну область памяти не могут одновременно указывать указатели разных типов. Это может сломать оптимизацию, но никак не приводит к UB. Приведение указателей к (void *) такого предупреждения не дает.

Стандарт на этот счет вроде молчит, поправьте, если ошибаюсь.

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

> Насколько я знаю, это GCC-специфичное предупреждение, причем оно связано с особенностями оптимизатора

оптимизатор оптимизирует (тем более по умолчанию) только тогда когда он уверен, что не изменит поведение корректного кода. Если он ломает код, то будь уверен, что компиляторные ребята ткнут тебя мордой где у тебя UB.

dilmah ★★★★★
()

В OpenCV еще на такое полагались

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

> не гарантируется. Вообще тут сразу имеет место быть варнинг в строчке

Варнинг этот про другое, как я написал раньше.

А вот почему "не гаратнируется"? Размещение в памяти элементов структуры может различаться в зависимости от этих элементов, то есть не быть строго последовательным?

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

> то есть не быть строго последовательным?

Что ты подразумеваешь под последовательным ? Если выравнивание - то да, могут быть фокусы.

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

подразумеваю пример ТС:

struct s1{
int a;
...
}

struct s2{
int a;
...
}

s1 p1 = {...};

s2 *p2 = (void *)p1;

выравнивание может привести к тому, что p1->a и p2->a будут ссылаться на разные области памяти?

SilentBob
()

Изучайте XLib.h и примеры программирования с XLib, пример практически оттуда срисован. Любое событие можно преобразовать в структуру XAnyEvent

typedef struct {
	int type;
	unsigned long serial;	/* # of last request processed by server */
	Bool send_event;	/* true if this came from a SendEvent request */
	Display *display;/* Display the event was read from */
	Window window;	/* window on which event was requested in event mask */
} XAnyEvent;
например XSelectionClearEvent
XSelectionClearEvent
typedef struct {
	int type;
	unsigned long serial;	/* # of last request processed by server */
	Bool send_event;	/* true if this came from a SendEvent request */
	Display *display;	/* Display the event was read from */
	Window window;
	Atom selection;
	Time time;
} XSelectionClearEvent;

или смешивая их в вместе

typedef union _XEvent {
        int type;		/* must not be changed; first element */
	XAnyEvent xany;
	XKeyEvent xkey;
	XButtonEvent xbutton;
	XMotionEvent xmotion;
 ....................

anonymous
()

Проблемы с выравниванием могут возникнуть, когда модули раздельно компилируются с разными параметрами.

anonymous
()

Не гарантируется. Есть нюансы, но в данном случае надо юзать union. Нет причин не юзать union.

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

> оптимизатор оптимизирует (тем более по умолчанию) только тогда когда он уверен, что не изменит поведение корректного кода.

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

6.3.2.3 Pointers 1 A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

Про UB там тоже есть: 7 A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned57) for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

в сочетании с "All pointers to structure types shall have the same representation and alignment requirements as each other." никакого UB в примере ТС нет. Равно как и "условие внутри if() никогда не выполнится". Чтобы компилятор в этом случае не ругался, вполне законным считается использование промежуточного приведения к (void *).

SilentBob
()

>У трёх структур общее "начало". Указатели на экземпляры структур s1 и s2 преобразовываются к указателям s_base. Гарантируется ли стандартом Си, что условие внутри if() никогда не выполнится?

Варианты в Си делаются через union.

union vals_t { double d; float f; integer i; ...whatever.. };

struct variant_t { int tag union vals_t vals; };

Absurd ★★★
()

так надежней всего :)

struct s_base
{
int tag;
};

struct s1
{
struct s_base base;
int data;
};

struct s2
{
struct s_base base;
float data;
};

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

ага, а обращение к tag вообще великолепно!

struct s2 s2_obj; s2_obj.base.tag = new_tag;

да-да! больше кода! некрасивого и бесполезного!

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

ну, не я первым начал извращение))
к тому же это куда красивее, чем:
struct s1 a;struct s_base *b = (struct s_base *) &a;
и потом, когда тебе понадобится что-то добавить в s_base, настанет большой песец.

хотите красивый код в данном случае - используйте С++

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

>хотите красивый код в данном случае - используйте С++

О да. Я даже знаю одну типобезопасную реализацию union-ов. Некоторым даже удалось ее скомпилировать. http://www.oonumerics.org/tmpw01/alexandrescu.pdf

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

Нормальный код, понятный, очевидный, 100% работающий, не вызывающий ни у кого вопросов и без дублирования.

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

можно еще проще, gcc умеет так:
struct s_base
{
int tag;
};

struct s1
{
struct s_base;
int data;
};

struct s2
{
struct s_base;
float data;
};

struct s1 a;
a.tag = 1;

anonymous
()
Ответ на: комментарий от alex4
#define STRUCT_DEF struct s_base \
{ \
int tag; \
}; \
struct s1 \
{ \
struct s_base base; \
int data; \
}; \
struct s2 \
{ \
struct s_base base; \
float data; \
};  \

#define GET_TAG(a) a.base.tag;

#define MAIN_F int main() \
{ \
    struct s2_obj; \
    GET_TAG(s2_obj) = new_tag; \
    return 0; \
} \

STRUCT_DEF
MAIN_F

так штоле?

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

А что нельзя через наследование все сделать. Есть базовая структура struct s_base, есть две структуры-потомки (struct s1 и struct s2) поле type в структуре предке. Компилятор тогда всё гарантирует.

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

Тогда юзайте что-то вроде __attribute__((packed)) и будет вам счастье (правда с ограничениями по портируемости и производительности).

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