Re: Это UB ?
От: T4r4sB Россия  
Дата: 01.07.24 07:40
Оценка: 1 (1) -4 :)
Здравствуйте, LaptevVV, Вы писали:

LVV>Создаем union

LVV>
union D{
LVV>int a;
LVV>char b[4];     // или std::byte b[4]
LVV>};
LVV>D temp;
LVV>temp.a = 1234;
LVV>char byte = temp.b[0];
LVV>

LVV>В прежние времена — делалось так.
LVV>А сейчас ?
LVV>Где-то у меня в мозгах зацепилось, что это UB.

Для чара — нет, не уб. И если внутри функции делается все то тоже не УБ
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Это UB ?
От: LaptevVV Россия  
Дата: 01.07.24 08:59
Оценка: +5 :)
σ>Бан в гугле — это не шутки…
Не хочешь отвечать — проходи молча мимо.
Здесь со знатоками советуются.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 01.07.24 09:04
Оценка: 1 (1) +1 -2
Здравствуйте, LaptevVV, Вы писали:

LVV>Размещающий new — не предлагать

LVV>reinterpret_cast — можно попробовать.

memcpy, как не странно.

Из указателя на int32_t (ну не int же все же, да?) в указатель на char (я бы взял unsigned char или uint8_t, нафига мне парить себе мосг, знаковый мне char сегодня попался или беззнаковый).

Компилятор понимает, что если memcpy делается для таких маленьких порций данных, то не надо превращать его в вызов библиотечной фуккции, которая копирует большие объемы со скоростью курьерского поезда, творческоги примененяя SSE-регистры
Re[2]: Это UB ?
От: watchmaker  
Дата: 05.07.24 01:44
Оценка: 22 (1) +2
Здравствуйте, B0FEE664, Вы писали:

BFE>Этот пример попадает под специальные гарантии

Не попадает

BFE>отсюда

BFE>

BFE>In a standard-layout union with an active member of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.


В обоих цитатах стандарта ты кажется почему-то пропустил важное условие: все эти гарантии даны только для членов из common initial sequence.
Для примера из первого поста длина этой последовательности — 0.

Процитированные примеры из стандарта показывают, как работать с tagged-union, и гарантируют эту работу.
Но это прямо совсем другой сценарий, который не связан с type punning и реинтерпретацией содержимого памяти.

Например, если есть набор структур "сетевой адрес"
struct Ipv4 {
  int protocol;  // always == 4
  uint32_t num;
  uint16_t port;
};

struct Ipv6 {
  int protocol;  // always == 6
  unsigned char octets[16];
  uint16_t port;
};

struct Local {
  int protocol;  // always == 1 
  char path[256];
};

и нужно реализовать полиморфизм в функции connect, с возможностью передать в неё любой тип адреса, то стандарт говорит, что это можно сделать, в том числе, написав так:
struct AbstractAddress {
  int protocol;
  char padding[60];
};

union AddressStorage {
  AbstractAddress aa;
  Ipv4 a4;
  Ipv6 a6;
  Local local;
};

void connect(const AddressStorage& addr);

И что теперь функция connect может всегда узнать, что в неё передали, если прочитает поле addr.aa.protocol.
А после того, как функция узнает значение этого поля, то она узнает какой тип сейчас активный и к каким полям можно дальше обращаться:
void connect(const AddressStorage& addr) {
  switch (addr.aa.protocol) {
    case 1: return connect_local(addr.local);
    case 4: return connect_ipv4(addr.a4);
    case 6: return connect_ipv6(addr.a6);
    default: throw std::domain_error("unknown protocol");
  }
}

Вот поле protocol — это и есть в данном случае common initial sequence.
И это поле читать всегда безопасно вне зависимости от того, что в union положили.
А разрешения безусловно обращаться, например, к addr.local.path[0] процитированные пункты не дают (в смысле, запреты/разрешения описываются в других местах).
Re: Это UB ?
От: Doom100500 Израиль  
Дата: 01.07.24 05:43
Оценка: 17 (2) +1
Здравствуйте, LaptevVV, Вы писали:

LVV>Создаем union

LVV>
union D{
LVV>int a;
LVV>char b[4];     // или std::byte b[4]
LVV>};
LVV>D temp;
LVV>temp.a = 1234;
LVV>char byte = temp.b[0];
LVV>

LVV>В прежние времена — делалось так.
LVV>А сейчас ?
LVV>Где-то у меня в мозгах зацепилось, что это UB.

https://en.cppreference.com/w/cpp/language/union

It is undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.


У нас на MSVC работает похожий код, написанный мной много лет назад по незнанию.

LVV>Размещающий new — не предлагать

LVV>reinterpret_cast — можно попробовать.

LVV>variant ?

LVV>Что-то не совсем то, что мне нужно.

Предлагают std::bit_cast:

https://en.cppreference.com/w/cpp/numeric/bit_cast

https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior
Спасибо за внимание
Re: Это UB ?
От: B0FEE664  
Дата: 04.07.24 23:04
Оценка: 16 (1) -2
Здравствуйте, LaptevVV, Вы писали:

LVV>Создаем union

LVV>
union D{
LVV>int a;
LVV>char b[4];     // или std::byte b[4]
LVV>};
LVV>D temp;
LVV>temp.a = 1234;
LVV>char byte = temp.b[0];
LVV>

LVV>В прежние времена — делалось так.
LVV>А сейчас ?
LVV>Где-то у меня в мозгах зацепилось, что это UB.

Этот пример попадает под специальные гарантии и тут нет UB:
отсюда

In a standard-layout union with an active member of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.
[Example 5:

struct T1 { int a, b; };
struct T2 { int c; double d; };
union U { T1 t1; T2 t2; };
int f() {
  U u = { { 1, 2 } };   // active member is t1
  return u.t2.c;        // OK, as if u.t1.a were nominated
}

— end example]


Собственно, описание union чуть ли не начинается с этого:

[Note 1: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence ([class.mem]), and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members; see [class.mem].
— end note]


Но важно помнить, что это только для standard-layout, а шаг влево или право — уже UB. В частности, ЕМНИП, битовые поля, которые так любят некоторые электронщики использовать в union для быстрого (де)кодирования бинарных данных, это потенциальное UB.
И каждый день — без права на ошибку...
Re: Это UB ?
От: Кодт Россия  
Дата: 04.07.24 11:17
Оценка: 6 (1) -1 :)
Здравствуйте, LaptevVV, Вы писали:

LVV>В прежние времена — делалось так.

LVV>А сейчас ?
LVV>Где-то у меня в мозгах зацепилось, что это UB.

можно покурить в сторону прачечной

https://en.cppreference.com/w/cpp/utility/launder

https://stackoverflow.com/questions/39382501/what-is-the-purpose-of-stdlaunder
https://stackoverflow.com/questions/56308110/using-stdlaunder-to-get-a-pointer-to-an-active-union-member-from-a-pointer-to
Перекуём баги на фичи!
Re[3]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 01.07.24 19:57
Оценка: 3 (1) -2
Здравствуйте, LaptevVV, Вы писали:

LVV>Очень интересно!

LVV>Но мне для школьников-студней надо.
LVV>bit_cast — вот прям самое оно!

Вот и вырастут у тебя студенты, которые думают, что bit_cast — это хитрая волшебная магия, которая делает хитрое волшебное преобразование, и написана она хитрыми волшебными волшебниками, чей ход мысли простому человеку не понять.

Все-таки надо немного понимать, как оно всё под капотом устроено. А то, получается, современное обучение программированию обходит стороной внутреннее устройство вещей, потому что это, типа, сложно очень, а вместо того студентов обучают разнообразным оберткам-интерфейсам к этому внутреннему устройству.

В результате мы получаем специалиста, который вместо того, чтобы усвоить блок фундаментальных знаний, кстати, в программировании довольно компактный и поддающийся усвоению за конечное время, пытается впихнуть в свою голову невпихуемое, а именно, достаточно детальное знание модных на настоящий момент фреймворков и библиотек. Что чрезвычайно повышает порог вхождения в профессию, причем без особой на то нужды. Кроме того, поддержание себя в "профессиональном тонусе" с точки зрения такого специалиста включает в себя слежение за переменчивой модой в области популярных фреймворков и библиотек и изучение новых по мере вхождения их в оборот, что отнимает массу сил и времени, опять же, без особой на то нужды.
Re[2]: Это UB ?
От: T4r4sB Россия  
Дата: 01.07.24 17:07
Оценка: -2 :)
Здравствуйте, vsb, Вы писали:

vsb>Здравствуйте, LaptevVV, Вы писали:


LVV>>А сейчас ?

LVV>>Где-то у меня в мозгах зацепилось, что это UB.

vsb>И сейчас UB. Хотя, наверное, работать везде будет. Но по правилам — в юнионах можно читать только те поля, куда ранее была запись.


На чары не распространяется правило strict aliasing
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[4]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 01.07.24 22:20
Оценка: +3
Здравствуйте, T4r4sB, Вы писали:

Pzz>>Сейчас компиляторы пошли хитрые. Заметив в твоей программе UB, он может вообще что угодно сделать. Причем не в том месте, где UB, а где сам захочет. Например, может счесть, что 2 + 2 == 5, или подсыпать слабительного в миску с кормом твоего кота.


TB>Это если UB воспринимать как чёрную магию.

TB>А на самом деле оптимизатору надо знать, в каких случаях не надо делать лишнее чтение из памяти. И придуманы всякие хитрые правила для этого.

Дело не в черной магии. Мне сам подход не очень нравится. Да, компиляторы, генерирующие эффективный код, нужны. Но правила игры стали столь сложными, что даже очень квалифицированному человеку трудно учесть все нюансы.

Во многих случаях лучше иметь предсказуемое, понятное поведение, чем сэкономить пару циклов процессора. Даже в очень высоконагруженных программах, работающих на пределе требований по эффективности код, реально требующий сверхэффективной оптимизации, редко составляет более 10% общей кодовой базы программы. Причем в хорошо спроектированной программе сложный код и быстрый код редко пересекаются.

И на мой взгляд, было бы лучше, чтобы компилятор помогал делать сложный код более предсказуемым, а не быстрый код более быстрым. Возможно, чем усложнять правила игры везде, следовало бы добавить явную ручку, позволяющую сказать компилятору, "вот в этом месте, пожалуйста, побыстрее, даже за счет усложнения". Но по умолчанию держать эту ручку выключенной.
Re[4]: Это UB ?
От: ononim  
Дата: 16.07.24 14:35
Оценка: -1 :))
vsb>https://en.cppreference.com/w/cpp/language/union
vsb>It is undefined behavior to read from the member of the union that wasn't most recently written
не люблю буквоедство, но давай тогда уж читать первоисточники, а не вольные пересказы:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the
object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new
type as described in 6.2.6 (a process sometimes called “type punning”). This might be a trap representation

Как видим, в оригинале пишут, что данное UB стандартизовано в пункте 6.2.6
Ну а 6.2.6 просто рассказывает как там инты на биты ложатся .
Конкретное UB там описано лишь для случаев когда пишешь в переменную внутри union'а, то значения байтов которые не относятся к этой переменной становятся unspecified.

6 When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.51) The value of a structure or union object is never a trap representation, even though the value of a member of the structure or union object may be a trap representation.
7 When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.

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

The size of a union is sufficient to contain the largest of its members. The value of at most one of the members can be stored in a union object at any time. A pointer to a union object, suitably converted, points to each of its members (or if a member is a bitfield, then to the unit in which it resides), and vice versa.

Итак, факт того что указатель на union == указателю на любой его член де факто означает что они там все в его начале тусят.

Но к этому надо добавить type aliasing rules, это отдельная история, но как T4r4sB написал — char * и void * из них исключены.
Как много веселых ребят, и все делают велосипед...
Отредактировано 16.07.2024 14:50 ononim . Предыдущая версия . Еще …
Отредактировано 16.07.2024 14:37 ononim . Предыдущая версия .
Отредактировано 16.07.2024 14:37 ononim . Предыдущая версия .
Re[2]: Это UB ?
От: rg45 СССР  
Дата: 02.07.24 12:29
Оценка: 80 (1) +1
Здравствуйте, ajanov, Вы писали:

A>
A>void foo(uint32_t x, std::uint8_t out[4])
A>{
A>    out[0] = x & 0x000000FF;
A>    out[1] = (x & 0x0000FF00) >> 8;
A>    out[2] = (x & 0x00FF0000) >> 16;
A>    out[3] = (x & 0xFF000000) >> 24;
A>}
A>


Немного оффтопа мимоходом. Очень опасная функция из серии "не верь глазам своим". Компилятор автоматически преобразовал объявление этой функции в void foo(uint32_t x, std::uint8_t* out) и, скорее всего, даже никаких предупреждений не выдал. Так что out здесь никакой не массив, а обычный указатель. А раз так, то в эту функцию можно передать массив ЛЮБОГО размера в качестве фактического параметра:

http://coliru.stacked-crooked.com/a/acf5ae2a080a66ef

#include <concepts>
#include <cstdint>

void foo(uint32_t x, uint8_t out[4])
{
    static_assert(std::same_as<uint8_t*, decltype(out)>);

    // . . .    
}

int main()
{
    uint8_t out[1]{};

    foo(42, out); // Oops! Заезд по памяти.
}


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

void foo(uint32_t x, uint8_t (&out)[4]);
--
Отредактировано 02.07.2024 12:54 rg45 . Предыдущая версия . Еще …
Отредактировано 02.07.2024 12:54 rg45 . Предыдущая версия .
Отредактировано 02.07.2024 12:35 rg45 . Предыдущая версия .
Отредактировано 02.07.2024 12:34 rg45 . Предыдущая версия .
Отредактировано 02.07.2024 12:31 rg45 . Предыдущая версия .
Отредактировано 02.07.2024 12:31 rg45 . Предыдущая версия .
Re: Это UB ?
От: vsb Казахстан  
Дата: 01.07.24 15:59
Оценка: 4 (2)
Здравствуйте, LaptevVV, Вы писали:

LVV>А сейчас ?

LVV>Где-то у меня в мозгах зацепилось, что это UB.

И сейчас UB. Хотя, наверное, работать везде будет. Но по правилам — в юнионах можно читать только те поля, куда ранее была запись.

LVV>Размещающий new — не предлагать

LVV>reinterpret_cast — можно попробовать.

LVV>variant ?

LVV>Что-то не совсем то, что мне нужно.

Могу ошибаться, но вроде можно прикастовать int* к char* (но не наоборот). В С это должно работать, насчёт С++ не уверен. Тут, правда, тоже нюансы могут быть типа сколько там байтов в int и сколько битов в char — тоже величины не определённые.

А так — 100% правильный вариант это битовая арифметика. Хороший компилятор должен распознать замысел и преобразовать всё в ожидаемый код.

Вот пример с godbolt:

void u32to8(uint32_t u32, uint8_t *u8) {
    u8[0] = u32 & 0xff;
    u8[1] = (u32 >> 8) & 0xff;
    u8[2] = (u32 >> 16) & 0xff;
    u8[3] = (u32 >> 24) & 0xff;
}


Это скомпилировалось в

u32to8(unsigned int, unsigned char*):
        str     r0, [r1]  @ unaligned
        bx      lr
main:


То бишь в одну инструкцию. А когда компилятор это инлайнит по месту вызова, там вообще ничего не остаётся.
Отредактировано 01.07.2024 16:00 vsb . Предыдущая версия .
Re[2]: Это UB ?
От: flаt  
Дата: 01.07.24 09:08
Оценка: -1 :)
Здравствуйте, Pzz, Вы писали:

Pzz>memcpy, как не странно.


И bit_cast это как раз memcpy.
Re[3]: Это UB ?
От: vsb Казахстан  
Дата: 01.07.24 17:19
Оценка: +2
Здравствуйте, T4r4sB, Вы писали:

LVV>>>А сейчас ?

LVV>>>Где-то у меня в мозгах зацепилось, что это UB.

vsb>>И сейчас UB. Хотя, наверное, работать везде будет. Но по правилам — в юнионах можно читать только те поля, куда ранее была запись.


TB>На чары не распространяется правило strict aliasing


https://en.cppreference.com/w/cpp/language/union

It is undefined behavior to read from the member of the union that wasn't most recently written

Кроме того

The details of that allocation are implementation-defined

То бишь теоретически ничего не мешает компилятору понавставлять каких-нибудь паддингов, если ему так захочется.
Re[3]: Это UB ?
От: T4r4sB Россия  
Дата: 02.07.24 13:06
Оценка: +2
Здравствуйте, rg45, Вы писали:



R>Так что, если и делать массивы параметрами функций, то только через ссылки:


R>
R>void foo(uint32_t x, uint8_t (&out)[4]);
R>


А лучше через std::array
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[4]: Это UB ?
От: reversecode google
Дата: 02.07.24 22:58
Оценка: +1 -1
TB>А лучше через std::array

последний писк этого сезона std::span
Re[2]: Это UB ?
От: B0FEE664  
Дата: 04.07.24 18:16
Оценка: -1 :)
Здравствуйте, σ, Вы писали:

σ>Бан в гугле — это не шутки…


Действительно. Впрочем очевидно, что смотреть надо на [basic.lval]/11.31
И каждый день — без права на ошибку...
Re[2]: А вот еще вопрос про UB
От: B0FEE664  
Дата: 29.07.24 10:03
Оценка: 17 (1)
Здравствуйте, LaptevVV, Вы писали:

LVV>Раньше можно было написать доступ к a[i][j] как a[i*n+j], где n — количество элементов в строке.

Не, так нельзя было, надо было кастить к типу указателя ((int*)a)[i*n+j]

LVV>Но мне интересно, в таком виде это UB или не UB ?

Это всегда было UB и даже когда этот трюк проходил, нужно было держать в голове, что компилятор не обязан "вытягивать" матрицу по строкам, т.е. значение *(((int*)a) + 1) могло быть как значением a[0][1], так и значением a[1][0], хотя я никогда не встречал, чтобы двумерный массив "вытягивали" по столбцам.
И каждый день — без права на ошибку...
Re: Это UB ?
От: σ  
Дата: 01.07.24 06:37
Оценка: :)
Бан в гугле — это не шутки…
Re[2]: Это UB ?
От: rg45 СССР  
Дата: 01.07.24 09:15
Оценка: :)
Здравствуйте, σ, Вы писали:

σ>Бан в гугле — это не шутки…


Ты личико сделай попроще, умник.
--
Re: Это UB ?
От: ajanov  
Дата: 02.07.24 09:56
Оценка: +1
Здравствуйте, LaptevVV, Вы писали:

LVV>Создаем union

LVV>
union D{
LVV>int a;
LVV>char b[4];     // или std::byte b[4]
LVV>};
LVV>D temp;
LVV>temp.a = 1234;
LVV>char byte = temp.b[0];
LVV>

LVV>В прежние времена — делалось так.
LVV>А сейчас ?
LVV>Где-то у меня в мозгах зацепилось, что это UB.

LVV>Размещающий new — не предлагать

LVV>reinterpret_cast — можно попробовать.

LVV>variant ?

LVV>Что-то не совсем то, что мне нужно.

В C — не UB, в C++ — UB начиная с C++11, но на x86 работает, по крайней метре MSVC и GCC, имеется легаси, которое может выдать сюрприз при обновлении компилятора. Для манипуляций с типами через битовое представление нужно использовать reinterpret_cast или std::bit_cast (с C++20). Последний учитывает особенности выравнивания.
В любом случае с такими манипуляциями программа перестает быть переносимой. Нужно получить представление целого побайтно от младшего к старшему? — то так:

void foo(uint32_t x, std::uint8_t out[4])
{
    out[0] = x & 0x000000FF;
    out[1] = (x & 0x0000FF00) >> 8;
    out[2] = (x & 0x00FF0000) >> 16;
    out[3] = (x & 0xFF000000) >> 24;
}
Re[5]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 02.07.24 11:28
Оценка: +1
Здравствуйте, LaptevVV, Вы писали:

Pzz>>В результате мы получаем специалиста, который вместо того, чтобы усвоить блок фундаментальных знаний, кстати, в программировании довольно компактный и поддающийся усвоению за конечное время, пытается впихнуть в свою голову невпихуемое, а именно, достаточно детальное знание модных на настоящий момент фреймворков и библиотек. Что чрезвычайно повышает порог вхождения в профессию, причем без особой на то нужды. Кроме того, поддержание себя в "профессиональном тонусе" с точки зрения такого специалиста включает в себя слежение за переменчивой модой в области популярных фреймворков и библиотек и изучение новых по мере вхождения их в оборот, что отнимает массу сил и времени, опять же, без особой на то нужды.


LVV>мОлодежь, как это сейчас у них принято, вопрошает: а нафига все это нужно?

LVV>И только по прошествии нескольких лет работы признаются (мне выпускники говорили), что становится видно чела с фундаментальной подготовкой и без оной.
LVV>И первые — лучшие работники, чем вторые...

Интеллект даден человеку, чтобы меньше руками работать

Вот затем и нужно — чтобы не учить 100500 унылых фреймворков, как угорелый, а просто понимать, как это всё работает там, унутре. И осознавать, что красивых слов много ("инновационная технология XXX, которая позволяет...") и постоянно появляются новые, а реально содержательных вещей мало. И многие новые технологии — это лишь новое имя давно известным вещам.

А с фреймворком можно ознакомиться по мере надобности, и побыстрее забыть эту дичь, когда надобность рассосется.
Re[2]: Это UB ?
От: reversecode google
Дата: 03.07.24 08:17
Оценка: -1
A>.... Нужно получить представление целого побайтно от младшего к старшему? — то так:

https://github.com/tip-of-the-week/cpp/blob/master/tips/263.md
Re[3]: Это UB ?
От: B0FEE664  
Дата: 15.07.24 18:02
Оценка: +1
Здравствуйте, LaptevVV, Вы писали:

WX>>Разве здесь не надо учитывать Big Endian/Little Endian?

LVV>Ну, мне такой универсальности не нужно. Я про это упоминаю.
LVV>На интеле по младшему адресу младший байт. А интел практически у всех студентов.

Тогда стоит упомянуть:
https://en.cppreference.com/w/cpp/types/endian
https://en.cppreference.com/w/cpp/numeric/byteswap
https://en.cppreference.com/w/cpp/numeric/bit_cast
И каждый день — без права на ошибку...
Re[6]: Это UB ?
От: ononim  
Дата: 17.07.24 08:44
Оценка: +1
Кстати еще моментик — тут утверждают (с ссылками):

GNU extensions to standard C++ (and to C90) do explicitly allow type-punning with unions.

У микрософта полагаю тоже самое будет, ибо у них struct LARGE_INTEGER например именно так и работает, и еще наверное много чего. Так что, в сферическом С++ это UB, а в чистом C, а так же C++/GNU и C++/MS — вполне легально
Как много веселых ребят, и все делают велосипед...
Re[2]: А вот еще вопрос про UB
От: σ  
Дата: 28.07.24 18:47
Оценка: +1
LVV>Раньше можно было написать доступ к a[i][j] как a[i*n+j], где n — количество элементов в строке.
Никогда нельзя было, даже в C

P.S. я, естественно, имею в виду подразумеваемый аффтором код, вроде ((int*)a)[i*n+j] (что некорректно для всех i, j, n), или более корректный (&a[0][0])[i*n+j] (это может быть ок для некоторых i, j, n (пока за a[0] не вылазим), но не для доступа ко "всему" двумерному массиву)
Отредактировано 29.07.2024 21:16 σ . Предыдущая версия .
Это UB ?
От: LaptevVV Россия  
Дата: 01.07.24 04:33
Оценка:
Создаем union
union D{
int a;
char b[4];     // или std::byte b[4]
};
D temp;
temp.a = 1234;
char byte = temp.b[0];

В прежние времена — делалось так.
А сейчас ?
Где-то у меня в мозгах зацепилось, что это UB.

Размещающий new — не предлагать
reinterpret_cast — можно попробовать.

variant ?
Что-то не совсем то, что мне нужно.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Отредактировано 01.07.2024 4:35 LaptevVV . Предыдущая версия .
Re[2]: Это UB ?
От: LaptevVV Россия  
Дата: 01.07.24 09:01
Оценка:
D>Предлагают std::bit_cast:
D>https://en.cppreference.com/w/cpp/numeric/bit_cast
О, спасибо! То, что нужно!
А то я в С++17 сижу и как-то на С++20 не посмотрел.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: Это UB ?
От: LaptevVV Россия  
Дата: 01.07.24 14:26
Оценка:
Pzz>memcpy, как не странно.
Pzz>Из указателя на int32_t (ну не int же все же, да?) в указатель на char (я бы взял unsigned char или uint8_t, нафига мне парить себе мосг, знаковый мне char сегодня попался или беззнаковый).
Да, это само собой.
Pzz>Компилятор понимает, что если memcpy делается для таких маленьких порций данных, то не надо превращать его в вызов библиотечной функции, которая копирует большие объемы со скоростью курьерского поезда, творчески применяя SSE-регистры
Очень интересно!
Но мне для школьников-студней надо.
bit_cast — вот прям самое оно!
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[4]: Это UB ?
От: T4r4sB Россия  
Дата: 01.07.24 17:43
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>То бишь теоретически ничего не мешает компилятору понавставлять каких-нибудь паддингов, если ему так захочется.


Это было бы implementation-defined
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 01.07.24 20:01
Оценка:
Здравствуйте, vsb, Вы писали:

LVV>>Где-то у меня в мозгах зацепилось, что это UB.


vsb>И сейчас UB. Хотя, наверное, работать везде будет. Но по правилам — в юнионах можно читать только те поля, куда ранее была запись.


Сейчас компиляторы пошли хитрые. Заметив в твоей программе UB, он может вообще что угодно сделать. Причем не в том месте, где UB, а где сам захочет. Например, может счесть, что 2 + 2 == 5, или подсыпать слабительного в миску с кормом твоего кота.
Re[3]: Это UB ?
От: T4r4sB Россия  
Дата: 01.07.24 20:21
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Сейчас компиляторы пошли хитрые. Заметив в твоей программе UB, он может вообще что угодно сделать. Причем не в том месте, где UB, а где сам захочет. Например, может счесть, что 2 + 2 == 5, или подсыпать слабительного в миску с кормом твоего кота.


Это если UB воспринимать как чёрную магию.
А на самом деле оптимизатору надо знать, в каких случаях не надо делать лишнее чтение из памяти. И придуманы всякие хитрые правила для этого.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[4]: Это UB ?
От: LaptevVV Россия  
Дата: 02.07.24 09:38
Оценка:
LVV>>Очень интересно!
LVV>>Но мне для школьников-студней надо.
LVV>>bit_cast — вот прям самое оно!видно чела с фундаментальной ап
Pzz>Вот и вырастут у тебя студенты, которые думают, что bit_cast — это хитрая волшебная магия, которая делает хитрое волшебное преобразование, и написана она хитрыми волшебными волшебниками, чей ход мысли простому человеку не понять.
Под капотом я все рассказываю
И дополнительный код для целых, и сдвиги вместо деления, и замену вычитания сложением сдопкодом и т.д.
Мне именно для подкапотом надо безопасно плавающее в биты разложить.
Без уб и прочих разных гадостей.

Pzz>В результате мы получаем специалиста, который вместо того, чтобы усвоить блок фундаментальных знаний, кстати, в программировании довольно компактный и поддающийся усвоению за конечное время, пытается впихнуть в свою голову невпихуемое, а именно, достаточно детальное знание модных на настоящий момент фреймворков и библиотек. Что чрезвычайно повышает порог вхождения в профессию, причем без особой на то нужды. Кроме того, поддержание себя в "профессиональном тонусе" с точки зрения такого специалиста включает в себя слежение за переменчивой модой в области популярных фреймворков и библиотек и изучение новых по мере вхождения их в оборот, что отнимает массу сил и времени, опять же, без особой на то нужды.


мОлодежь, как это сейчас у них принято, вопрошает: а нафига все это нужно?
И только по прошествии нескольких лет работы признаются (мне выпускники говорили), что становится видно чела с фундаментальной подготовкой и без оной.
И первые — лучшие работники, чем вторые...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 02.07.24 11:35
Оценка:
Здравствуйте, ajanov, Вы писали:

A>В любом случае с такими манипуляциями программа перестает быть переносимой. Нужно получить представление целого побайтно от младшего к старшему? — то так:


A>
A>void foo(uint32_t x, std::uint8_t out[4])
A>{
A>    out[0] = x & 0x000000FF;
A>    out[1] = (x & 0x0000FF00) >> 8;
A>    out[2] = (x & 0x00FF0000) >> 16;
A>    out[3] = (x & 0xFF000000) >> 24;
A>}
A>


Второй уже пример такого рода в этой теме.

Лаптев спрашивал, как получить побайтное представление целого, "как на хосте". Эта же функция возвращает побайтное представление целого в little endian.

Разные задачи решаем, товарищи. Разработчик должен быть наблюдателен и не брезглив.
Re[3]: Это UB ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 02.07.24 21:46
Оценка:
Здравствуйте, rg45, Вы писали:

R>
R>    foo(42, out); // Oops! Заезд по памяти.
R>


Это у компилятора получилось потому, что функция и вызов ее в одной единице трансляции. Но нонешние компиляторы пошли умные, и если функция и вызов в одной единице транспяции, они и без всякого статик-ассерта сообразят переполнение буфера проверить.

Поэтому фукнцию лучше заинлайнить, а статик ассерт можно и вовсе убрать. Заодно эффективность повысится. При удачном стечении обстоятельств компилятор может так ее заинлайнить, что от нее и вовсе ничего не останется.
Re[3]: Это UB ?
От: ajanov  
Дата: 03.07.24 05:13
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Лаптев спрашивал, как получить побайтное представление целого, "как на хосте". Эта же функция возвращает побайтное представление целого в little endian.


Так boost::endian::native_uint32_buf_t в помощь, но хочется в теории покопаться
Re[4]: Это UB ?
От: rg45 СССР  
Дата: 03.07.24 07:18
Оценка:
Здравствуйте, Pzz, Вы писали:

R>>
R>>    foo(42, out); // Oops! Заезд по памяти.
R>>


Pzz>Это у компилятора получилось потому, что функция и вызов ее в одной единице трансляции. Но нонешние компиляторы пошли умные, и если функция и вызов в одной единице транспяции, они и без всякого статик-ассерта сообразят переполнение буфера проверить.


Pzz>Поэтому фукнцию лучше заинлайнить, а статик ассерт можно и вовсе убрать. Заодно эффективность повысится. При удачном стечении обстоятельств компилятор может так ее заинлайнить, что от нее и вовсе ничего не останется.


Блин, я фигею, и эти люди еще стыдят других за ненаблюдательность.

Во-первых, тот статик ассерт использован чисто для иллюстрации того, что массив превращается в указатель больше ни для чего. Посмотри еще раз что именно проверяется тем статик ассертом и подумай, может ли в нем быть хоть какой-нибудь практический смысл. Он же проверяет то, чего НЕ должно быть, по задумке программиста.

А во-вторых, я же привел конкретный пример, в котором есть все перечисленные тобой условия: и современный компилятор, и одна единица трансляции:

http://coliru.stacked-crooked.com/a/517f0033b9d50dac

#include <iostream>
#include <cstdint>

void foo(uint32_t x, uint8_t out[4])
{
    static_assert(std::same_as<uint8_t*, decltype(out)>);

    out[0] = x & 0x000000FF;
    out[1] = (x & 0x0000FF00) >> 8;
    out[2] = (x & 0x00FF0000) >> 16;
    out[3] = (x & 0xFF000000) >> 24;
}

int main()
{
    uint8_t out[1]{};

    foo(42, out); // Oops!

    std::cout << "Hello, World! Everything is OK" << std::endl;
}


Ну и что, и где обещанное "сообразят переполнение буфера проверить"?
--
Отредактировано 03.07.2024 7:29 rg45 . Предыдущая версия . Еще …
Отредактировано 03.07.2024 7:22 rg45 . Предыдущая версия .
Re[2]: Это UB ?
От: B0FEE664  
Дата: 04.07.24 22:23
Оценка:
Здравствуйте, σ, Вы писали:

σ>Бан в гугле — это не шутки…


Хмм, судя по реакции на предыдущее
Автор: B0FEE664
Дата: 04.07 21:16
, таки есть те, кто верит гуглу, а не стандарту.
И каждый день — без права на ошибку...
Re[3]: Это UB ?
От: B0FEE664  
Дата: 05.07.24 09:25
Оценка:
Здравствуйте, watchmaker, Вы писали:

BFE>>Этот пример попадает под специальные гарантии

W>Не попадает
не убедили.

W>В обоих цитатах стандарта ты кажется почему-то пропустил важное условие: все эти гарантии даны только для членов из common initial sequence.

W>Для примера из первого поста длина этой последовательности — 0.
Этот аргумент принимается, но есть ещё [basic.lval]/11.31:

If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
— the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
— a char, unsigned char, or std​::​byte type.

Мы пытаемся доступиться к stored value (char byte = temp.b[0];) объекта типа union

union D
{
int a;
char b[4]; // или std::byte b[4]
};

через char и мы знаем, что адреса a и b совпадают:

[Note 2: A union object and its non-static data members are pointer-interconvertible ([basic.compound], [expr.static.cast]).
As a consequence, all non-static data members of a union object have the same address.
— end note]

при этом данные в массиве D::b лежат непрерывным куском...

Если есть где-то запрет, то его хотелось бы увидеть и понять.
И каждый день — без права на ошибку...
Re[4]: Это UB ?
От: B0FEE664  
Дата: 05.07.24 09:44
Оценка:
Здравствуйте, T4r4sB, Вы писали:

Pzz>>Сейчас компиляторы пошли хитрые. Заметив в твоей программе UB, он может вообще что угодно сделать. Причем не в том месте, где UB, а где сам захочет. Например, может счесть, что 2 + 2 == 5, или подсыпать слабительного в миску с кормом твоего кота.


TB>Это если UB воспринимать как чёрную магию.

UB — это не чёрная магия, а стиль мышления оптимизаторов, которые в погоне за скоростью уходят за грань разумности.
И каждый день — без права на ошибку...
Re: Это UB ?
От: Worminator X Россия #StandWithPalestine 🖤🤍💚
Дата: 06.07.24 03:12
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>D temp;

LVV>temp.a = 1234;
LVV>char byte = temp.b[0];

Разве здесь не надо учитывать Big Endian/Little Endian?
Как запру я тебя за железный замок, за дубовую дверь окованную,
Чтоб свету божьего ты не видела, мое имя честное не порочила…
М. Лермонтов. Песня про царя Ивана Васильевича, молодого опричника и удалого купца Калашникова
Re[2]: Это UB ?
От: vopl Россия  
Дата: 06.07.24 08:23
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, LaptevVV, Вы писали:


LVV>>В прежние времена — делалось так.

LVV>>А сейчас ?
LVV>>Где-то у меня в мозгах зацепилось, что это UB.

К>можно покурить в сторону прачечной


К>https://en.cppreference.com/w/cpp/utility/launder


К>https://stackoverflow.com/questions/39382501/what-is-the-purpose-of-stdlaunder

К>https://stackoverflow.com/questions/56308110/using-stdlaunder-to-get-a-pointer-to-an-active-union-member-from-a-pointer-to

На первый взгляд кажется тут прачечная не применима, так как она требует чтобы целевой объект был в своем лайфтайме (https://timsong-cpp.github.io/cppwp/ptr.launder#2.sentence-2), а в нашем случае это не так, целевой объект вне своего лайфтайма так как не явяется активным (https://timsong-cpp.github.io/cppwp/class.union.general#2).
Re[2]: Это UB ?
От: LaptevVV Россия  
Дата: 06.07.24 08:33
Оценка:
LVV>>D temp;
LVV>>temp.a = 1234;
LVV>>char byte = temp.b[0];
WX>Разве здесь не надо учитывать Big Endian/Little Endian?
Ну, мне такой универсальности не нужно. Я про это упоминаю.
На интеле по младшему адресу младший байт. А интел практически у всех студентов.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Это UB ?
От: σ  
Дата: 16.07.24 09:00
Оценка:
σ>>Бан в гугле — это не шутки…

BFE>Хмм, судя по реакции на предыдущее
Автор: B0FEE664
Дата: 04.07 21:16
, таки есть те, кто верит гуглу, а не стандарту.


В смысле, в гугле сказали про https://timsong-cpp.github.io/cppwp/n4868/basic.lval#11.3, а ты и поверил?
Ок, доступа к гуглу ещё недостаточно, нужно ещё мозги иметь, чтобы фильтровать что там находится
Re[5]: Это UB ?
От: watchmaker  
Дата: 16.07.24 19:42
Оценка:
Здравствуйте, ononim, Вы писали:

vsb>>https://en.cppreference.com/w/cpp/language/union

O>не люблю буквоедство, но давай тогда уж читать первоисточники

И в качестве "первоисточника" приводится стандарт совсем другого языка. :facepalm:

С и С++ разошлись достаточно далеко, чтобы в них появились множество конструкций, которые в тексте программы записываются одинаково, но означают совсем разные вещи или результат вычисления которых отличается.

> а не вольные пересказы


Тем не менее, если возникнет желание прочитать "вольный пересказ" про union в языке С, то его можно найти по адресу
en.cppreference.com/w/c/language/union,
а не адресу
en.cppreference.com/w/cpp/language/union.
Re[6]: Это UB ?
От: ononim  
Дата: 16.07.24 20:50
Оценка:
vsb>>>https://en.cppreference.com/w/cpp/language/union
O>>не люблю буквоедство, но давай тогда уж читать первоисточники
W>И в качестве "первоисточника" приводится стандарт совсем другого языка.
Ну вот опять оказывается тут обсуждение про С++
Я не помню когда в плюсовом коде юзал union, имхо если вам хочется его юзать, вам следует писать на С
Как много веселых ребят, и все делают велосипед...
Re: А вот еще вопрос про UB
От: LaptevVV Россия  
Дата: 28.07.24 17:06
Оценка:
Раньше можно было написать доступ к a[i][j] как a[i*n+j], где n — количество элементов в строке.
Ныне я получаю вот такое:
int A[3][4]{{1,2,3,4},{2,3,4,5},{3,4,5,6}};
      int n = size(A[0]);
      cout << A[2][1] << ' '
           << A[2*n+1] << endl;       // по-любому выдает адрес

С++17, minGW, g++ 8.1
Как понимаю, где-то это прописали.
Я, конечно, понимаю, что можно что-то вроде размещающего new применить.
Но мне интересно, в таком виде это UB или не UB ?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: А вот еще вопрос про UB
От: kov_serg Россия  
Дата: 28.07.24 19:15
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>Раньше можно было написать доступ к a[i][j] как a[i*n+j], где n — количество элементов в строке.

LVV>Ныне я получаю вот такое:
LVV>
LVV>int A[3][4]{{1,2,3,4},{2,3,4,5},{3,4,5,6}};
LVV>      int n = size(A[0]);
LVV>      cout << A[2][1] << ' '
LVV>           << A[2*n+1] << endl;       // по-любому выдает адрес
LVV>

LVV>С++17, minGW, g++ 8.1
LVV>Как понимаю, где-то это прописали.
LVV>Я, конечно, понимаю, что можно что-то вроде размещающего new применить.
LVV>Но мне интересно, в таком виде это UB или не UB ?
Вы об этом?
void test() {
    int a0[4]={11,12,13,14};
    int a1[4]={21,22,23,24};
    int a2[4]={31,32,33,34};
    int* A[3]={a0,a1,a2};
    int B[3][4]={{11,12,13,14},{21,22,23,24},{31,32,33,34}};
    printf("A1=%d\n",(int)((char*)A[1]-(char*)A[0]));
    printf("A2=%d\n",(int)((char*)A[2]-(char*)A[0]));
    printf("B1=%d\n",(int)((char*)B[1]-(char*)B[0]));
    printf("B2=%d\n",(int)((char*)B[2]-(char*)B[0]));
}
A1=16
A2=32
B1=16
B2=32

Я бы не стал полагаться на такое поведение.
Re[3]: А вот еще вопрос про UB
От: LaptevVV Россия  
Дата: 29.07.24 13:40
Оценка:
LVV>>Но мне интересно, в таком виде это UB или не UB ?
BFE>Это всегда было UB и даже когда этот трюк проходил, нужно было держать в голове, что компилятор не обязан "вытягивать" матрицу по строкам, т.е. значение *(((int*)a) + 1) могло быть как значением a[0][1], так и значением a[1][0], хотя я никогда не встречал, чтобы двумерный массив "вытягивали" по столбцам.
В фортране так было — сам на нем писал.
Во всех других языках было по строкам.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.