Про приведение типов

Green
Offline
Зарегистрирован: 01.10.2015

Уж сколько твердили об этом, однако случаются грабельки.(
Давеча имеем такой код:

  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 в данном случае?

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

Проблемка упрощённо описывается в:
uint32_t key = digitalRead(0) << 15u;
Внимание, вопрос. Чему будет равен key в данном случае?

0x8000 ?

Green
Offline
Зарегистрирован: 01.10.2015

Вот и я считал что так должно быть. Однако, 0xFFFF8000.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Это как то связано с привидением типов или просто нужно было предварительно инициализировать переменную ?

Green
Offline
Зарегистрирован: 01.10.2015

Ну конечно с привЕдением. Переменная инициализирована.

Green
Offline
Зарегистрирован: 01.10.2015

Ну, а почему никто не спрашивает "пачиму"?
unsigned long = bool << unsigned int. Что происходит?

sadman41
Offline
Зарегистрирован: 19.10.2016

Пачиму bool?

int digitalRead(uint8_t pin) {...}

_Igor_
Offline
Зарегистрирован: 10.01.2022

1. Неявное приведение типов - жди сюрпризов. 2. А что вы хотите? 0х8000 - это +32768 (uint16), но и в такой же степени  -32768(int16). 0хFFFF8000 - это и есть -32768(int32). Для компилятора int/uint важно только для арифметики, ну и для вывода, но не в неявном приведении при присваивании, как написан компилятор - так и будет...

Green
Offline
Зарегистрирован: 01.10.2015

Да. Извиняюсь. Хотя bool было бы логичнее. Но, не важно. 

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Green пишет:

Ну, а почему никто не спрашивает "пачиму"?
unsigned long = bool << unsigned int. Что происходит?

Постфактум то конечно понятно. особенно после этого:

Green пишет:
Вот и я считал что так должно быть. Однако, 0xFFFF8000.

А напороться на это очень легко можно.

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

Если сделать так: 

uint32_t key = (uint32_t) digitalRead(0) << 15u;

То наверное все будет как ожидается?

Может стоит закрепить этот пример для напоминания?

sadman41
Offline
Зарегистрирован: 19.10.2016

Шифт-операции со знаковыми переменными вообще штука своеобразная.

Green
Offline
Зарегистрирован: 01.10.2015

SergeiL пишет:

Если сделать так: 

uint32_t key = (uint32_t) digitalRead(0) << 15u;

То наверное все будет как ожидается?


Естественно, всё будет как и ожидается.

sadman41 пишет:
Шифт-операции со знаковыми переменными вообще штука своеобразная.

Вот только хотелось бы понимать "чё происходит").

sadman41
Offline
Зарегистрирован: 19.10.2016
Green
Offline
Зарегистрирован: 01.10.2015

Ну нет. Это, всё же, определённое поведение. Но тут без 100 грамм/без ЕвгенийП не обойтись. Который может ткнуть в пункт талмуда)
Смущает что int << unsigned int = чему? (какому типу?) Вот в этом и есть понимание проблемы, КМК.

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, может, выразился криво. Если компилятор знаешь, то определённое. А если нет - неопределенное.

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.

Green
Offline
Зарегистрирован: 01.10.2015

Ну, я считал что int << unsigned int должно быть приведено к unsigned int. А в моём случае, unsigned int уже должно быть преобразовано в unsigned long. Но что то пошло не так... Не было преобразования int в unsigned int. "пчему?" Ведь один операнд то есть unsignend int!

sadman41
Offline
Зарегистрирован: 19.10.2016

Вроде как по дефолту к знаковому числу с наибольшей разрядностью приводится.

Green
Offline
Зарегистрирован: 01.10.2015

Да, по дефолту приводится к числу с большей разрядностью. Т.е. int должен приводиться к unsigned int. У меня 2 операнда. Один int, другой unsigned int. Вот здесь у меня затык.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Такие затыки призван решать static_cast<>

Green
Offline
Зарегистрирован: 01.10.2015

Да решений то море. Нужно понимать проблему. В чём недопонимание.)

sadman41
Offline
Зарегистрирован: 19.10.2016

unsigned int и int же имеют одинаковую разрядность, но разные max и min.

Green
Offline
Зарегистрирован: 01.10.2015

Разрядность то одинакова. Только 1 бит на знак. И с этого всё и начинается.

sadman41
Offline
Зарегистрирован: 19.10.2016

Но разрядность одинакова. Формально все верно - оба числа одной разрядности, значит переходим ко второй инструкции - суем все в знаковую переменную.

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Так а с чего int  должен преобразовываться в unsigned int?

При сдвиге просто сдвигаются разряды, а тип переменной не меняется.  

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

SergeiL пишет:

Так а с чего int  должен преобразовываться в unsigned int?

При сдвиге просто сдвигаются разряды, а тип переменной не меняется.  

видимо так и есть только не в лживом Вашем С )))

Green
Offline
Зарегистрирован: 01.10.2015

SergeiL пишет:

Так а с чего int  должен преобразовываться в unsigned int?

При сдвиге просто сдвигаются разряды, а тип переменной не меняется.  

Богатырёв. Программирование на С под UNIX.
 
9.2. Правила преобразований типов.
9.2.1. В выражениях.
Если операнд имеет тип не int и не double, то сначала приводится:
    signed   char  --> int  расширением знакового бита (7)
    unsigned char  --> int  дополнением нулями слева
             short --> int  расширением знакового бита (15)
    unsigned short --> unsigned int   дополнением нулями слева
             enum  --> int  порядковый номер в перечислимом типе
             float --> double   дробная часть дополняется нулями
Если любой операнд имеет тип double, то и другой операнд приводится к типу double. 
Результат: типа double.
 
Запишем все дальнейшие преобразования в виде схемы:
если есть                   то другой               результат 
операнд типа                приводится к типу       имеет тип
if(double)                  -->double               double
else if(unsigned long)      -->unsigned long        unsigned long
else if(long)               -->long                 long
else if(unsigned int)       -->unsigned int         unsigned int
else оба операнда имеют тип int                     int
 
У меня один из операндов unsigned int, значит результат должен тоже должен быть unsigned int. Или нет?
SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Green пишет:

Богатырёв. Программирование на С под UNIX.
 
9.2. Правила преобразований типов.
9.2.1. В выражениях.
Если операнд имеет тип не int и не double, то сначала приводится:
    signed   char  --> int  расширением знакового бита (7)
    unsigned char  --> int  дополнением нулями слева
             short --> int  расширением знакового бита (15)
    unsigned short --> unsigned int   дополнением нулями слева
             enum  --> int  порядковый номер в перечислимом типе
             float --> double   дробная часть дополняется нулями
Если любой операнд имеет тип double, то и другой операнд приводится к типу double. 
Результат: типа double.
 
Запишем все дальнейшие преобразования в виде схемы:
если есть                   то другой               результат 
операнд типа                приводится к типу       имеет тип
if(double)                  -->double               double
else if(unsigned long)      -->unsigned long        unsigned long
else if(long)               -->long                 long
else if(unsigned int)       -->unsigned int         unsigned int
else оба операнда имеют тип int                     int
 
У меня один из операндов unsigned int, значит результат должен тоже должен быть unsigned int. Или нет?

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

Например в арифметических операциях, операциях сравнения, присвоения. Помню, что правя часть >> будет по разному обрабатываться для знакового отрицательного и беззнакового (заполняться знаком или нулем).

А в случае сдвига влево, у нас нет необходимости  преобразования типа левой части оператора << 

Тут просто выполняется сдвиг левой части на N бит в пределах установленного типа данных.     

Green
Offline
Зарегистрирован: 01.10.2015

SergeiL пишет:
Тут просто выполняется сдвиг левой части на N бит в пределах установленного типа данных.     


Где это описано? Хотелось бы знать.

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Green пишет:

SergeiL пишет:
Тут просто выполняется сдвиг левой части на N бит в пределах установленного типа данных.     


Где это описано? Хотелось бы знать.

Не знаю где это описано, я доки очень давно читал, последние 25 лет в другой области.

Может знающие люди подскажут.

А так, вроде логично.

Например есть у нас такой вариант:

int result;
unsigned long j = 3;
int i = -3; 
result = i>>j;

Мы же понимаем, что при сдвиге мы не должны потерять знак в i?

То, что j беззнаковая и long, не значит же, что i мы должны преобразовать к  unsigned long.

 

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Поискал, не нашел четкого описания по данному поводу.

Наверное не там искал :-(