Про приведение типов
- Войдите на сайт для отправки комментариев
Пт, 15/04/2022 - 10:09
Уж сколько твердили об этом, однако случаются грабельки.(
Давеча имеем такой код:
uint32_t key = 0; for (uint8_t c = 0, n = 0; c < sizeof(cols); c++) { on_col(cols[c]); for (uint8_t r = 0; r < sizeof(rows); r++, n++) key |= get_key(rows[r]) << n; off_col(cols[c]); }
Проблемка упрощённо описывается в:
uint32_t key = digitalRead(0) << 15u;
Внимание, вопрос. Чему будет равен key в данном случае?
Проблемка упрощённо описывается в:
uint32_t key = digitalRead(0) << 15u;
Внимание, вопрос. Чему будет равен key в данном случае?
0x8000 ?
Вот и я считал что так должно быть. Однако, 0xFFFF8000.
Это как то связано с привидением типов или просто нужно было предварительно инициализировать переменную ?
Ну конечно с привЕдением. Переменная инициализирована.
Ну, а почему никто не спрашивает "пачиму"?
unsigned long = bool << unsigned int. Что происходит?
Пачиму bool?
int digitalRead(uint8_t pin) {...}
1. Неявное приведение типов - жди сюрпризов. 2. А что вы хотите? 0х8000 - это +32768 (uint16), но и в такой же степени -32768(int16). 0хFFFF8000 - это и есть -32768(int32). Для компилятора int/uint важно только для арифметики, ну и для вывода, но не в неявном приведении при присваивании, как написан компилятор - так и будет...
Да. Извиняюсь. Хотя bool было бы логичнее. Но, не важно.
Ну, а почему никто не спрашивает "пачиму"?
unsigned long = bool << unsigned int. Что происходит?
Постфактум то конечно понятно. особенно после этого:
А напороться на это очень легко можно.
Еще раз уяснил для себя, что при любых операциях с разными типами данных обязательно нужно делать явное их преобразование к одному типу, вот чтобы такого не получилось случайно.
Если сделать так:
То наверное все будет как ожидается?
Может стоит закрепить этот пример для напоминания?
Шифт-операции со знаковыми переменными вообще штука своеобразная.
Если сделать так:
То наверное все будет как ожидается?
Естественно, всё будет как и ожидается.
Вот только хотелось бы понимать "чё происходит").
Если кратко: неопределенное поведение.
https://wiki.sei.cmu.edu/confluence/display/c/INT13-C.+Use+bitwise+opera...
https://stackoverflow.com/questions/7522346/right-shift-and-signed-integer
Ну нет. Это, всё же, определённое поведение. Но тут без 100 грамм/без ЕвгенийП не обойтись. Который может ткнуть в пункт талмуда)
Смущает что int << unsigned int = чему? (какому типу?) Вот в этом и есть понимание проблемы, КМК.
Ну, может, выразился криво. Если компилятор знаешь, то определённое. А если нет - неопределенное.
Bitwise operators should be used only with unsigned integer operands, as the results of bitwise operations on signed integers are implementation-defined.
implementation-defined behavior [ISO/IEC 9899:2011]
Unspecified behavior whereby each implementation documents how the choice is made.
Ну, я считал что int << unsigned int должно быть приведено к unsigned int. А в моём случае, unsigned int уже должно быть преобразовано в unsigned long. Но что то пошло не так... Не было преобразования int в unsigned int. "пчему?" Ведь один операнд то есть unsignend int!
Вроде как по дефолту к знаковому числу с наибольшей разрядностью приводится.
Да, по дефолту приводится к числу с большей разрядностью. Т.е. int должен приводиться к unsigned int. У меня 2 операнда. Один int, другой unsigned int. Вот здесь у меня затык.
Такие затыки призван решать static_cast<>
Да решений то море. Нужно понимать проблему. В чём недопонимание.)
unsigned int и int же имеют одинаковую разрядность, но разные max и min.
Разрядность то одинакова. Только 1 бит на знак. И с этого всё и начинается.
Но разрядность одинакова. Формально все верно - оба числа одной разрядности, значит переходим ко второй инструкции - суем все в знаковую переменную.
Так а с чего int должен преобразовываться в unsigned int?
При сдвиге просто сдвигаются разряды, а тип переменной не меняется.
Так а с чего int должен преобразовываться в unsigned int?
При сдвиге просто сдвигаются разряды, а тип переменной не меняется.
видимо так и есть только не в лживом Вашем С )))
Так а с чего int должен преобразовываться в unsigned int?
При сдвиге просто сдвигаются разряды, а тип переменной не меняется.
Например в арифметических операциях, операциях сравнения, присвоения. Помню, что правя часть >> будет по разному обрабатываться для знакового отрицательного и беззнакового (заполняться знаком или нулем).
А в случае сдвига влево, у нас нет необходимости преобразования типа левой части оператора <<
Тут просто выполняется сдвиг левой части на N бит в пределах установленного типа данных.
Где это описано? Хотелось бы знать.
Где это описано? Хотелось бы знать.
Не знаю где это описано, я доки очень давно читал, последние 25 лет в другой области.
Может знающие люди подскажут.
А так, вроде логично.
Например есть у нас такой вариант:
Мы же понимаем, что при сдвиге мы не должны потерять знак в i?
То, что j беззнаковая и long, не значит же, что i мы должны преобразовать к unsigned long.
Поискал, не нашел четкого описания по данному поводу.
Наверное не там искал :-(