Y>>void f()
Y>>{
Y>> union
Y>> {
Y>> int a;
Y>> const char* p;
Y>> };
Y>> a = 1;
Y>> p = "Jennifer";
Y>> /// то a уже не можем быть равна 1
Y>> char buf[100];
Y>> memcpy( &a, buf, strlen("Jennifer\0") ) /// в предыдущем посте тут ошибочку зделал
Y>> ///*buf == *p;
Y>>}
Y>>
"ошибочку зделал", но так и не исправил полностью
Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, rusted, Вы писали:
R> ... R>"ошибочку зделал", но так и не исправил полностью R>Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
R> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1.
Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
По стандарту адрес a и адрес *p равны
R> Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
Меня и интересует это, в каких случаях это будет работать в каких нет И правильно ли это по стандарту ?
Re[10]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
R>> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Y>Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
Сказано же — с оптимизацией. Разработчики компилятора могли прочитать стандарт буквально, и компилятор может подразумевать, что переменные a и p не связаны логически.
Оптимизация может, например, поместить копию значения из а в регистр процессора, чтобы два раза и памяти не читать.
Y>По стандарту адрес a и адрес *p равны
По стандарту равны адреса членов юниона — т.е. в вашем случае это a и p.
В вашем "ошибочку зделал" вы переписываете строку "Jennifer\0" прямо поверх содержимого юниона (а не по адресу, хранящемуся в а, как вам, очевидно, хотелось бы). При этом вы практически гарантированно выходите за его границу и перетираете данные, расположенные далее в стеке (длина строки Jennifer — 8 байт + терминальный нуль, что как минимум на 1 байт больше, чем размер пойнтера в 64-битной машине).
Кстати, у строковых констант терминальный нуль указывать не нужно, он автоматически вставляется компилятором.
R>> Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу. Y>Меня и интересует это, в каких случаях это будет работать в каких нет И правильно ли это по стандарту ?
По стандарту это правильно.
В каких случаях это будет работать? Зависит от того, что именно вы подразумеваете под словами "это" и "работать".
Если "это" означает, что вы пишете в один из членов юниона, а потом читаете из другого — это может работать, а может и нет. Зависит от того, какой именно результат вы хотите получить и как именно вы хотите его использовать.
В качестве примера работающего кода, использующего юнионы, могу предложить такую вот функцию:
bool IsLittleEndianCPU()
{
union {
int a;
char b[sizeof(int)];
} u;
u.a = 1;
return u.b[0] != 0;
}
Этот код работает, и даже почти безопасен (с оговоркой про гипотетические оптимизации компилятора).
Жопа этого кода в том, что он бесполезен и не нужен в реальной жизни: порядок байт в слове известен еще в процессе компиляции, и нет никакого смысла его определять в рантайме.
Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Re[11]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
R>>> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Y>>Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
Х>Сказано же — с оптимизацией. Разработчики компилятора могли прочитать стандарт буквально, и компилятор может подразумевать, что переменные a и p не связаны логически.
Да они могут быть не связаны логически они связаны одним адресом
Х>Оптимизация может, например, поместить копию значения из а в регистр процессора, чтобы два раза и памяти не читать.
Y>>По стандарту адрес a и адрес *p равны
Х>По стандарту равны адреса членов юниона — т.е. в вашем случае это a и p.
да согласен.
Х>В вашем "ошибочку зделал" вы переписываете строку "Jennifer\0" прямо поверх содержимого юниона (а не по адресу, хранящемуся в а, как вам, очевидно, хотелось бы). При этом вы практически гарантированно выходите за его границу и перетираете данные, расположенные далее в стеке (длина строки Jennifer — 8 байт + терминальный нуль, что как минимум на 1 байт больше, чем размер пойнтера в 64-битной машине). Х>Кстати, у строковых констант терминальный нуль указывать не нужно, он автоматически вставляется компилятором.
Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Х>По стандарту это правильно. Х>В каких случаях это будет работать? Зависит от того, что именно вы подразумеваете под словами "это" и "работать". Х>Если "это" означает, что вы пишете в один из членов юниона, а потом читаете из другого — это может работать, а может и нет. Зависит от того, какой именно результат вы хотите получить и как именно вы хотите его использовать.
Х>В качестве примера работающего кода, использующего юнионы, могу предложить такую вот функцию:
Х>
Х>Этот код работает, и даже почти безопасен (с оговоркой про гипотетические оптимизации компилятора). Х>Жопа этого кода в том, что он бесполезен и не нужен в реальной жизни: порядок байт в слове известен еще в процессе компиляции, и нет никакого смысла его определять в рантайме.
Причем здесь попугаи ой порядок следования байт?
Х>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Здравствуйте, Yotce, Вы писали:
Y>Да они могут быть не связаны логически они связаны одним адресом
Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Я тоже заметил и тоже понял.
Но это, к сожалению, не просто опечатка, т.к. ровно ту же ошибку вы воспроизвели в "исправленном" варианте.
Кроме того, из ваших пояснений к коду видно, что ваше понимание пойнтеров и работы с ними, скажем так, не отличается кристальной ясностью (путаете адрес переменной и ее содержимое, не видите проблемы в том, что размеры указателей разных типов могут отличаться, спокойно используете int в качестве контейнера для указателя). Именно поэтому я не прошел мимо, как Rusted, а попытался объяснить подробнее.
Y>Причем здесь попугаи ой порядок следования байт?
Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
Х>>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Y>А где написано что так делать нельзя?
"Делать так" — это, возвращаясь к вашему первому посту, использовать обычный указатель, совмещенный с областью хранения указателя на нестатическую функцию-член класса, для чтения и модификации этого указателя на нестатическую функцию-член класса.
В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Текст стандарта только подтверждает вышесказанное.
Y>здесь
Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом?
Re[13]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
Y>>Да они могут быть не связаны логически они связаны одним адресом
Х>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Можно конкретные где это написано в стандарте?
Y>>Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Х>Я тоже заметил и тоже понял. Х>Но это, к сожалению, не просто опечатка, т.к. ровно ту же ошибку вы воспроизвели в "исправленном" варианте. Х>Кроме того, из ваших пояснений к коду видно, что ваше понимание пойнтеров и работы с ними, скажем так, не отличается кристальной ясностью (путаете адрес переменной и ее содержимое, не видите проблемы в том, что размеры указателей разных типов могут отличаться, спокойно используете int в качестве контейнера для указателя). Именно поэтому я не прошел мимо, как Rusted, а попытался объяснить подробнее.
Не будем разводить демагогию.
Y>>Причем здесь попугаи ой порядок следования байт?
Х>Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
В моём понимании машина не знает да ей и все равно как компилятор представляет указатель на метод класса.
Х>>>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Y>>А где написано что так делать нельзя?
Х>"Делать так" — это, возвращаясь к вашему первому посту, использовать обычный указатель, совмещенный с областью хранения указателя на нестатическую функцию-член класса, для чтения и модификации этого указателя на нестатическую функцию-член класса.
Я НЕ где НЕ утверждал что это правильно.
union
{ int ( __stdcall IObject::*Method )( int, float );
void *void_func;
};
Я видел такое преобразование в библиотеки "Rpc — чего там" к сожалению не помню её названия, щас её найти не могу.
Вот и возник вопрос о том на какие грабли можно наступить если использовать такую запись. То что sizeof ( void_func ) != sizeof ( Methos ) и так понятно. Если-бы этого не знал то и вопроса не было-бы...
Но почему Вы говорите что нельзя скопировать?
Х>В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Вы говорите, что Х>Текст стандарта только подтверждает вышесказанное.
можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Y>>здесь
Х>Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом?
В моем понимании этот код иллюстрирует сохранение указателя на функцию-член класса
Re[14]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Х>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>Можно конкретные где это написано в стандарте?
9.5 Unions [class.union]
1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time.
Сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Y>>>Причем здесь попугаи ой порядок следования байт?
Х>>Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
Y>В моём понимании машина не знает да ей и все равно как компилятор представляет указатель на метод класса.
К чему это?
В приведенном мной примере нет ни одного указателя.
Y>Я НЕ где НЕ утверждал что это правильно. Y>
Y> union
Y> { int ( __stdcall IObject::*Method )( int, float );
Y> void *void_func;
Y> };
Y>
Y>Я видел такое преобразование в библиотеки "Rpc — чего там" к сожалению не помню её названия, щас её найти не могу. Y>Вот и возник вопрос о том на какие грабли можно наступить если использовать такую запись. То что sizeof ( void_func ) != sizeof ( Methos ) и так понятно. Если-бы этого не знал то и вопроса не было-бы...
В таком случае ответ для вас должен быть очевиден, и я не понимаю, зачем вы здесь разводите дискуссию в стиле "поднимите мне веки и процитируйте стандарт".
Грабли — как обычно:
1. На одном компиляторе этот код работает, при попытке перейти на другой компилятор/ОС/архитектуру процессора он может привести к глюкам программы.
2. Даже если код заработает под другим компилятором в тестовом примере, не факт, что в реальном большом проекте он не вызовет появление странных глюков.
В принципе, этого достаточно, чтобы закрыть дискуссию о том, имеет ли смысл использовать такие трюки с юнионами в рабочем коде.
Что касается кода библиотеки — там свои причуды. Она, скорее всего, разрабатывалась под конкретный компилятор и ОС, поэтому авторы не сильно пеклись о переносимости кода и о соблюдении стандартов. Работает — и ладно.
Y>Но почему Вы говорите что нельзя скопировать?
Потому что в приведенном вами коде копирование происходит потенциально неполное: копируется только часть "большого" указателя (то, что поместилось в обычный).
Х>>В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Y>Вы говорите, что Х>>Текст стандарта только подтверждает вышесказанное. Y>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
О, мне не трудно, я повторю еще раз:
9.5 Unions [class.union]
1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time.
Для меня сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Y>>>здесь
Х>>Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом? Y> В моем понимании этот код иллюстрирует сохранение указателя на функцию-член класса
Искал там слово union, не нашел.
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
Х>>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>>Можно конкретные где это написано в стандарте?
Х>Вам уже писали
Х>9.5 Unions [class.union]
Х>1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
Х>most one of the non-static data members can be stored in a union at any time.
Х>Сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Для меня сие означает, В объединение, не более одного из не-статические члены данных может быть активен в любое время, то есть,
в любое время может хранить не более одного из не-статических членов данных.
Я не претендую что это дословный перевод, но тут негде не говорится что писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
В моём понимании тут говорят про то что если
union __u_t
{
static int s_a; /// static memberint a; /// non-static memberchar c; /// non-static member
} obj;
int __u_t::s_a = 10;
obj.a= 100;
obj.c='c';
/// И для меня сие означает что нельзя рассчитывать на то что a=100;
Здравствуйте, Yotce, Вы писали:
Х>>Текст стандарта только подтверждает вышесказанное. Y>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Вот у тебя есть например:
union
{
бомба,
картофелина
}
Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
В стандарте это описано тут:
3.10/10 If a program attempts to access the stored value of an object through a glvalue of other than one of the
following types the behavior is undefined:
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type similar (as defined in 4.4) to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
of the object,
— an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic
data members (including, recursively, an element or non-static data member of a subaggregate
or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
Как можно земетить, в некоторых случаях так можно делать.
Re[16]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Для меня сие означает, В объединение, не более одного из не-статические члены данных может быть активен в любое время, то есть, Y>в любое время может хранить не более одного из не-статических членов данных.
"Активен" подразумевает доступ к данным, а не просто их пассивное хранение. Впрочем, это несущественная тонкость.
По сути, запись данных через один мембер, а чтение через другой, эквивалентно приведению "сырого" указателя на данные к целевому типу с последующим разыменованием.
Т.е. примерно так:
#include <iostream>
using namespace std;
int main()
{
union {
float a;
int b;
} u;
u.a = 100.0;
cout << "u.b=" << u.b << endl;
// эквивалентный метод доступа - через касты по сырому указателю
cout << "casts=" << *reinterpret_cast<int*>(reinterpret_cast<void*>(&u.a)) << endl;
}
Вывод:
u.b=1120403456
casts=1120403456
Тут надо обратить внимание на то, что данные берутся "сырые" — т.е. компилятор не знает ничего об этих данных, и берет их "как есть". В некоторых случаях это приводит к неопределенному поведению (где конкретно — говорит стандарт
).
Например, при работе с указателями вовнутрь объекта с множественным наследованием, компилятор иногда пользуется своим знанием об объекте-потомке. При этом компилятор может подправить указатель так, чтобы он смотрел на нужную часть объекта. Если лезть к указателю через поле в юнионе, никаких аджастментов не будет. Пример правки указателей компилятором — тут.
И кстати, если в юнионе хранится нетривиальный объект (с конструктором), то никаких автоматических вызовов конструктора-деструктора при первом доступе к полю компилятор не сгенерирует (я это к содержимому вот этого вашего поста
). Надеюсь, вы в том посте пошутили, и в голове у вас все-таки не такой беспросветный мрак.
В юнионе нельзя хранить нетривиальные объекты (non-POD). Точнее, в стандарте С++11 кое-какие поблажки сделали, но несущественные (никаких автоматических вызовов конструктора нет и не будет).
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Fuz, Вы писали:
Fuz>Здравствуйте, Yotce, Вы писали:
Х>>>Текст стандарта только подтверждает вышесказанное. Y>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Fuz>Вот у тебя есть например: Fuz>
Fuz>Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
В том-то и дело туда с начало положили бомбу потом её заменили на картофель, почему там должна быть бомба?
Fuz>В стандарте это описано тут:
Fuz>
3.10/10
Fuz>If a program attempts to access the stored value of an object through a glvalue of other than one of the
Fuz>following types the behavior is undefined:
Fuz> — the dynamic type of the object,
Fuz> — a cv-qualified version of the dynamic type of the object,
Fuz> — a type similar (as defined in 4.4) to the dynamic type of the object,
Fuz> — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
Fuz> — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
Fuz> of the object,
Fuz> — an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic
Fuz> data members (including, recursively, an element or non-static data member of a subaggregate
Fuz> or contained union),
Fuz> — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
Fuz> — a char or unsigned char type.
Не понял причём тут это?
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Здравствуйте, Fuz, Вы писали:
Fuz>>Здравствуйте, Yotce, Вы писали:
Х>>>>Текст стандарта только подтверждает вышесказанное. Y>>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Fuz>>Вот у тебя есть например: Fuz>>
Fuz>>Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
Y>В том-то и дело туда с начало положили бомбу потом её заменили на картофель, почему там должна быть бомба?
Я про этот случай (здесь замены нет):
union {
float a;
int b;
} u;
u.a = 100.0; // положили бомбуint v = u.b; // ... а достаем как картошку. Но по факту там бомба
'u.b' — это (в нашем случае) initializer expression, которое представляет собой lvalue типа 'int' (5.2.5/3), но объект, на который оно ссылается('u.a') имеет тип 'float' (в union ведь может только один объект храниться)
При выполнении value initialization будет выполнено lvalue-to-rvalue преобразование. Здесь появится UB, т.к. типы не соответствуют (ожидается 'int', а на входе 'float').
Re[17]: union { int (Foo::*cFunc ) (); void *void_func;}
Если Кто-то утверждает что трава "зелёная-зелёная" кто-то поверит на слова, а кто-то попытается разобраться в этом. И выяснят что трава "зелёная-зеленая" при определённых обстоятельствах, а ещё бывает "зеленая"... .
Re[16]: union { int (Foo::*cFunc ) (); void *void_func;}