2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3, 4, 5  След.
 
 Re: c++ задача на битовые операции
Сообщение27.11.2019, 10:05 
Заслуженный участник


02/08/11
6874
guryev, вам надо научиться работать с гипертекстом. Когда слова написаны синим цветом вот так:
Цитата:
access to an object through a pointer of a different type
- это называется "гиперссылка". На неё можно нажать левой кнопкой мыши, и прочитать много интересного. Но я боюсь, что вам это всё равно не поможет, потому что там много текста, а вы, видимо, не особо настроены на чтение.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение27.11.2019, 10:38 
Заслуженный участник
Аватара пользователя


06/10/08
6422
guryev в сообщении #1427917 писал(а):
Examples of undefined behavior are ... access to an object through a pointer of a different type, etc.
В общем случаае да, но конкретно для char * в стандарте сделано исключение.
Поэтому-то выше и говорили, что это работает с char, но не работает с uint32_t.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение27.11.2019, 11:35 
Заслуженный участник
Аватара пользователя


16/07/14
8354
Цюрих
Seman в сообщении #1427905 писал(а):
Почему не использовать буфер какой из std::byte или даже std::vector<std::byte> ?
Потому что нам в него нужно положить сформированные другим кодом данные. А там могут быть и инты, и POD структуры (и даже не POD, но тогда придется смириться с нетривиальной сериализацией).
Seman в сообщении #1427905 писал(а):
это абсолютно надуманная ситуация
Нет, подобные штуки регулярно происходят в реальном коде (не очень качественном - т.к. соблюдение strict aliasing всё же штука обычно локальная). Просто записи и чтения раскиданы по 10 разным функциям.
Seman в сообщении #1427905 писал(а):
Полагал, что
Цитата:

0
2
т.к. (и ваш и мой код именно это и выдает, что разумеется "не ожидаемо") и
Цитата:

0
1
-- это верно(ожидаемо).
Нет, как раз 0 2 - это ожидаемо. 0 1 означает, что запись в int_32* ничего не поменяла.
И ваш код, как и мой, выдает ожидаемый результат при компиляции без оптимизации и не ожидаемый с -O2.
guryev в сообщении #1427914 писал(а):
относится к Undefined Behavior. Просто в данном случае это безопасно
Эта надпись хорошо смотрелась бы на надгробном камне.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение27.11.2019, 17:40 


28/10/19
5
arseniiv в сообщении #1426980 писал(а):
Видимо, не одну функцию, а пару:
Используется синтаксис C++
char get_byte(long a, int index)
long set_byte(long a, int index, char new_value)
где a — это число, воспринимаемое как массив. Первая функция выдаёт значение одного из байтов, вторая заменяет и возвращает новое число-массив, в котором заменили старое на новое.
Да, именно это и надо было сделать)

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение27.11.2019, 23:16 


29/12/13
306
guryev в сообщении #1427914 писал(а):
Ничем. Я же говорю, что дело не в обращении к u.c[0], а в том, как это применяется:

Если ничем. То вопрос снят, у вас также. И делать как вы советуете нельзя. Правильно? Или не также?
guryev в сообщении #1427914 писал(а):
Здесь в little-endian архитектуре выведется 'x', а в big-endian - нулевой байт.

Нет. Гарантировано как там нулевым байтом, гарантировано в нем будет 'x' 120(0x78).
guryev в сообщении #1427914 писал(а):
Что будет, если обратиться к 1230-му элементу четырёхэлементного массива? При чём здесь это? Вы исходное сообщение читали?

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

-- 27.11.2019, 23:48 --

mihaild в сообщении #1427929 писал(а):
Потому что нам в него нужно положить сформированные другим кодом данные. А там могут быть и инты, и POD структуры (и даже не POD, но тогда придется смириться с нетривиальной сериализацией).

Ничего непонял. Зачем вынимать для этого половинку из int64_t , четыре байта из long и т.д. И даже если вдруг действительно где понабились отдельные байты или биты, делается это обычно сдвигом.

mihaild в сообщении #1427929 писал(а):
Нет, как раз 0 2 - это ожидаемо. 0 1 означает, что запись в int_32* ничего не поменяла.
И ваш код, как и мой, выдает ожидаемый результат при компиляции без оптимизации и не ожидаемый с -O2.

Ничего не понял. Вы же его привели как пример чего-то неправильного? Т.е. такого, чего не должно быть. Или зачем?
Вы передаете один и тот же адрес в функцию в int32_t *i32, int64_t *i64 , потом пишите в него сначала 1, затем 2, затем возвращаете из него значение 2.
Я переписал в C-шном стиле, для наглядности. Причем тут reinterpret_cast? Это можно без всяких reinterpret_cast и приведений типов переделать,
два char*, char* в функцию друг на друга. Тоже самое получите.
https://onlinegdb.com/Hy6Ygv2nr
Сам код не демонстрирует никаких проблем с reinterpret_cast или приведением типов, а тем более UB.

-- 27.11.2019, 23:55 --

Xaositect в сообщении #1427922 писал(а):
Цитата:
Examples of undefined behavior are ... access to an object through a pointer of a different type, etc.

общем случаае да, но конкретно для char * в стандарте сделано исключение.
Поэтому-то выше и говорили, что это работает с char, но не работает с uint32_t.


Если не затруднит, можно ссылку на стандарт где именно так access to an object through a pointer of a different type ?
И в любом случае, даже если там такое, и правда есть, всегда можно в char , std::byte и из них во что угодно.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 00:11 
Заслуженный участник
Аватара пользователя


16/07/14
8354
Цюрих
Seman в сообщении #1427990 писал(а):
Зачем вынимать для этого половинку из int64_t , четыре байта из long и т.д.
Изначально был вопрос про "зачем вынимать байты". Интерпретировать одну и ту же область памяти как значения двух разных сложных типов нужно и правда сильно реже.
Seman в сообщении #1427990 писал(а):
И даже если вдруг действительно где понабились отдельные байты или биты, делается это обычно сдвигом
Собирать числа из байтов сдвигами долго. А более сложные структуры - и вообще невозможно.
Seman в сообщении #1427990 писал(а):
Вы же его привели как пример чего-то неправильного?
Именно.
Seman в сообщении #1427990 писал(а):
Тоже самое получите.
Нет, не то же самое.
Мой код https://www.onlinegdb.com/r1Rab4i3B и ваш https://www.onlinegdb.com/Hy-_2Vinr содержат UB.
Если бы не было strict aliasing, то разумно было бы ожидать вывод 0 2 в обоих случаях: мы записываем в младшие биты x число 2, старшие как были нулевыми, так и остались, так что логично, что функция вернет 2. И если компилировать без оптимизации, то так и получается.
Если же скомпилировать код с -O2, то компилятор решает, что запись в int32_t не может повлиять на int64_t, поэтому можно не перечитывать значение *i64, а просто сразу вернуть то, что туда только что записали. И в результате функция возвращает 1.
Seman в сообщении #1427990 писал(а):
Ничего не понял.
Вы, кажется, упустили, что UB в данном случае проявляется наглядно только при компиляции с оптимизацией.
Seman в сообщении #1427990 писал(а):
Причем тут reinterpret_cast?
Не при чем. Доступ к данным одного типа через несовместимый с ними указатель - это UB, независимо от способа получения этого указателя.
Seman в сообщении #1427990 писал(а):
два char*, char* в функцию друг на друга. Тоже самое получите. https://onlinegdb.com/Hy6Ygv2nr
Вот этот код как раз не содержит UB, и гарантировано напечатает 1 2. Т.к. в f оба аргумента указывают на значения одного типа, компилятор не имеет право на оптимизацию, аналогичную первому случаю.

-- 28.11.2019, 00:18 --

Seman в сообщении #1427990 писал(а):
Если не затруднит, можно ссылку на стандарт где именно так access to an object through a pointer of a different type ?
Стандарт, 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: <...>
Там длинный список, основная идея - можно брать базовые классы, добавлять константность, или читать побайтово (как char), больше ничего нельзя.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 01:41 


29/12/13
306
mihaild в сообщении #1427995 писал(а):
Изначально был вопрос про "зачем вынимать байты". Интерпретировать одну и ту же область памяти как значения двух разных сложных типов нужно и правда сильно реже.
Не совсем. Изначально, я привел ссылку на статью с куском из стандартных рекомендаций. Там говорилось, что не надо использовать union'ы таким способом как это предлагал guryev. Там был пример, где получают кусок char(1 байт) от int(32бита обычно, 4-байта), т.е. речь шла именно получить кусок от одной переменной. А ещё заметил, что непонятно, что и как реально хотят от ТС. Я не хотел вступать в дискуссии. Дальше мне пришлось пояснить, что процитированный мной кусок из стандартных рекомендаций. И ещё, что там ясно сказано, что доступ через reinterpret_cast к объектам с другим типом , есть определенное поведение("Accessing the result of an reinterpret_cast to a different type from the objects declared type is defined behavior"), не ub, в отличии от через union'ы. А также заметил, что авторы удивляются(is discouraged), что такое обескураживает, даже через reinterpret_cast, хотя это не UB, а я так вообще не понимаю зачем такое может понадобится.

mihaild в сообщении #1427995 писал(а):
Собирать числа из байтов сдвигами долго. А более сложные структуры - и вообще невозможно.

Собирать числа из байтов не надо. А более сложные структуры тем более. Для большинства задач есть стандартные типы. Минимальный в байт это char. Если чего нет, вы определяете свою структуру/класс и с ним работаете, помещаете её в подходящие контейнеры или куда хотите. Если вдруг, байт слишком крупный, надо работать с каким timestamp файловой системы, вынуть сдвигами, поместить в свою структуру и работать с ней.

mihaild в сообщении #1427995 писал(а):
Если же скомпилировать код с -O2, то компилятор решает, что запись в int32_t не может повлиять на int64_t, поэтому можно не перечитывать значение *i64, а просто сразу вернуть то, что туда только что записали. И в результате функция возвращает 1.

Да и правда, и с -O3 тоже, только что проверил, что вы сразу не сказали. Я думал удивляет, как раз нормальный вывод. :facepalm:

mihaild в сообщении #1427995 писал(а):
Доступ к данным одного типа через несовместимый с ними указатель - это UB, независимо от способа получения этого указателя.
Может быть, я не спорю(хотя и сомневаюсь, пока не увижу, или не найду это в стандарте). Но, что к char можно и из char точно можно, и это не UB , вопросов нет?
Вобщем, поправил изначальный свой пример вот так:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <iostream>

using namespace std;

int main()
{
    uint64_t tt=0x600000005 ;
        char* tt1 = reinterpret_cast<char*>(&tt);
        uint32_t tt32_1 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
        uint32_t tt32_2 = *(reinterpret_cast<uint32_t*>(&(tt1[4]))) ;
        std::cout << tt32_1 << std::endl;
        std::cout << tt32_2 << std::endl;
    return 0;
}
 

5,6
https://onlinegdb.com/S169EF22B
Так все, UB нету?
Только, если куда, что передавать надумаете, то либо переменные(tt32_1,tt32_2), либо *(reinterpret_cast<uint32_t*>(&(tt1[0]))) :lol:

И ещё, изначально было, что "Если же захотите uint64_t разбить на два uint32_t, появляются сложности.", я сказал, что сложностей не вижу. И я их не вижу. Нет сложностей не передавать их в функцию специально особо изощренным способом, чтобы свести с ума компилятор, сложностей нет. И уж на крайний случай -O0 тоже сложностей нет. Но я не говорил, что это надо делать, наоборот, убежден, что не надо.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 11:32 
Заслуженный участник
Аватара пользователя


16/07/14
8354
Цюрих
Seman в сообщении #1427997 писал(а):
Для большинства задач есть стандартные типы. Минимальный в байт это char. Если чего нет, вы определяете свою структуру/класс и с ним работаете, помещаете её в подходящие контейнеры или куда хотите.
У меня есть своя структура. А теперь мне надо её записать на диск или передать по сети. А ostream::write принимает char* и отказывается по умолчанию писать мой тип. Вот и приходится работать сохранять сложный тип в последовательность байт, а потом восстанавливать.
Seman в сообщении #1427997 писал(а):
что вы сразу не сказали

mihaild в сообщении #1427929 писал(а):
и не ожидаемый с -O2

Seman в сообщении #1427997 писал(а):
пока не увижу, или не найду это в стандарте
Я выше дал ссылку - пункт 3.10.10 стандарта.
Seman в сообщении #1427997 писал(а):
Так все, UB нету?
Есть.
Seman в сообщении #1427997 писал(а):
Нет сложностей не передавать их в функцию специально особо изощренным способом, чтобы свести с ума компилятор, сложностей нет
Есть. Здесь простой пример для наглядности. Реально такой код получится после какой-то непонятной подстановки шаблонов, перестановки операций и еще неизвестного количества оптимизаций. См. What Every C Programmer Should Know About Undefined Behavior. Компилятор активно пользуется предположением, что в коде нет UB, и его наличие может привести к самым странным последствиям.
Seman в сообщении #1427997 писал(а):
И уж на крайний случай -O0 тоже сложностей нет
Это тоже ничего, кроме замедления работы, не гарантирует.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 11:55 


05/09/16
11467
mmarin в сообщении #1427966 писал(а):
Да, именно это и надо было сделать)

Сделали, получилось?

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 16:27 


29/12/13
306
mihaild в сообщении #1428011 писал(а):
Я выше дал ссылку - пункт 3.10.10 стандарта.

Извините, но я не вижу ни ссылки, ни такого пункта в стандарте.
http://www.open-std.org/jtc1/sc22/wg21/ ... /n4713.pdf
mihaild в сообщении #1428011 писал(а):
Есть.

Откуда? Приведение к char* и из char* не является UB в любом случае, так?
mihaild в сообщении #1428011 писал(а):
А ostream::write принимает char* и отказывается по умолчанию писать мой тип. Вот и приходится работать сохранять сложный тип в последовательность байт, а потом восстанавливать.

Так если нельзя приводить то типы, то получается никак. Но я говорил, про вынимание частей из long, uint64_t и подобные манипуляции.

-- 28.11.2019, 16:28 --

mihaild в сообщении #1428011 писал(а):
. What Every C Programmer Should Know About Undefined Behavior
.


Мы же про с++ говорим, не так ли ?

-- 28.11.2019, 16:30 --

mihaild в сообщении #1428011 писал(а):
Реально такой код получится после какой-то непонятной подстановки шаблонов, перестановки операций и еще неизвестного


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

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 16:38 
Заслуженный участник


02/08/11
6874
Seman в сообщении #1428058 писал(а):
Приведение к char* и из char* не является UB в любом случае, так?
Приведение к char * не является, а вот приведение из char * почти всегда UB.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 16:40 


29/12/13
306
Seman в сообщении #1428058 писал(а):
Извините, но я не вижу ни ссылки, ни такого пункта в стандарте.


Туплю. Уже нашел. Но я не разработчик С++ компиляторов, я не понимаю эту семантику gvalue и т.д. а Вы точно понимаете? В рекомендациях сказано, что не UB.

-- 28.11.2019, 16:46 --

Цитата:
A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.

https://medium.com/@barryrevzin/value-c ... 6ae54bccbe

-- 28.11.2019, 16:49 --

warlock66613 в сообщении #1428061 писал(а):
Приведение к char * не является, а вот приведение из char * почти всегда UB.


Хорошо, осталось, чтобы это подтвердить на 100% продемонстрировать с реальным, желательно не надуманным примером.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 17:10 
Заслуженный участник
Аватара пользователя


16/07/14
8354
Цюрих
Seman в сообщении #1428058 писал(а):
Извините, но я не вижу ни ссылки, ни такого пункта в стандарте.
Я видимо брал какую-то другую сборку. По вашей ссылке это пункт 8.2.1.11. Что легко находится поиском по pdf.
Seman в сообщении #1428058 писал(а):
Приведение к char* и из char* не является UB в любом случае, так?
Приведение указателей UB не является. А вот разыменование - может, в зависимости от того, что реально лежит по этому адресу.
Seman в сообщении #1428058 писал(а):
Но я говорил, про вынимание частей из long, uint64_t и подобные манипуляции
А я - про вынимание байт.
Seman в сообщении #1428058 писал(а):
Мы же про с++ говорим, не так ли ?
Почти всё, что там написано, применимо и к С++.
Seman в сообщении #1428058 писал(а):
Ничего никак не получится, если дальше пользовать нормально переменные tt32_1, tt32_2, без указателей и дальнейштх неправильных манипуляций с ними
Вы статью по ссылке-то прочитайте.
Не бывает "безопасного" UB. Из реалистичного - компилятор может увидеть, что вы пытаетесь читать из int32_t, но в него никто ничего не писал, так что чтения не произойдет (ну мало ли, исключение где-то вылетит до него), и вообще выкинуть все инструкции, начиная с последнего момента, где могло бы выброситься исключение.
И примерно такое даже на практике происходит - компилятор, увидев, что из цикла без побочных эффектов можно выйти только в одном месте, выкидывает этот цикл и сразу переходит к выходу, забивая на случай, когда цикл бесконечный.
Seman в сообщении #1428062 писал(а):
Хорошо, осталось, чтобы это подтвердить на 100% продемонстрировать с реальным, желательно не надуманным примером
Нет, чтобы это подтвердить, надо дать ссылку на стандарт, что уже сделано. И даже приведен пример кода, который работает неожиданно. Сильно подозреваю, что и в реальных проектах несложно найти такой пример, но оставлю этот поиск вам.
Seman в сообщении #1428062 писал(а):
В рекомендациях сказано, что не UB
В рекомендациях - про то, как делать type punning. Через приведение указателей он в некоторых случаях безопасен (см. выше в каких). Через union он небезопасен.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение28.11.2019, 17:30 
Экс-модератор
Аватара пользователя


23/12/05
12046
В жизни я бы делал через union, в крайнем случае через приведение типов. Но можно работать с масками и сдвигами, чтобы выковыривать и засовывать в long отдельные байты.

 Профиль  
                  
 
 Re: c++ задача на битовые операции
Сообщение29.11.2019, 18:31 


29/12/13
306
mihaild в сообщении #1428063 писал(а):
Нет, чтобы это подтвердить, надо дать ссылку на стандарт, что уже сделано.
Нет. UB в случае с приведением типов через reinterpret_cast в моем коде никак вами необоснованно.
Просто ссылки на стандарт недостаточно. В стандарте:
Цитата:
11 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:
—(11.1)the dynamic type of the object,
—(11.2)a cv-qualified version of the dynamic type of the object,
—(11.3)a type similar (as defined in 7.5) to the dynamic type of the object,
—(11.4)a type that is the signed or unsigned type corresponding to the dynamic type of the object,
—(11.5)a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic typeof the object,
—(11.6)an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate orcontained union),
—(11.7)a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
—(11.8)a char,unsigned char, orstd::bytetype.

Как я понимаю эту фразу на русском:
Цитата:
Если программа пытается получить доступ к хранящемуся значению объекта посредством glvalue типа отличного от одного из следующих типов,то поведение неопределено:
... перечесление случаев, когда поведение определено(исключений) ...

Чтобы что-то обосновать, надо минимум :
  • Показать и обосновать, где у меня в коде glvalue и доступ к значению какого объекта я через него получаю.
  • Показать и обосновать, почему случай не попадает ни под одно из перечисленных исключений.
  • Предложить алтернативу и доказать, что с ней нет проблем.
И желательно показать, как конкретно этот случай(приведенный вами пример, не имеет отношения к этому случаю) с моим кодом проявляет UB на практике, на реальном компиляторе.

На данный момент, по факту. У нас есть пример из стандартных рекомендаций из int в auto:
Используется синтаксис C++
auto p = reinterpret_cast<unsigned char*>(&x);   // (2)
 

Причем подчеркивается, что такой доступ через reinterpret_cast является определенным поведением.
Дальше, приведу пример из книги Страуструпа, наоборот из char в int *:
Используется синтаксис C++
char x = 'a';
int∗p1 = &x;//error : no implicit char* to int* conversion
int∗p2 = static_cast<int>(&x);  //error : no implicit char* to int* conversion
int∗p3 = reinterpret_cast<int>(&x);        //OK: on your head be it
 

https://anekihou.se/programming/2.%20intermediete.pdf (стр. 302)

Цитата:
но оставлю этот поиск вам

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

Цитата:
В рекомендациях - про то, как делать type punning.

Каламбур типов/типизации(type punning), так называют трюки такие как доступ к части переменной через union(что показывал guryev).
Так вот, в рекомендациях по моей ссылке разбирается именно этот трюк, и говорится, что через union не рекомендуется, т.к. UB, а через reinterpret_cast, - можно и UB нету. При этом даже через union, часто используется и на практике всегда работает.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 68 ]  На страницу Пред.  1, 2, 3, 4, 5  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group