C/C++

Неявное приведение типов в С

Тут представлены правила неявного приведения:
У меня есть пару вопросов:
1) Если ранг y signed int и unsigned int одинаковый, то исходное выражение конвертируется в unsigned?
В кодблокс я написал такой код:

 #include  

int main(void)
{
int i = -100;
unsigned int u = 190U;
int a = u + i;
printf("%lld\n", a);
return 0;
}
output:

 27583791803400282 
Какая тут логика? То есть i приводится к типу unsigned int и эта единичка в самом начале остается и получается этот миллиард складывается с 190U и все заносится в a?
2) Почему если переменная 'a' будет типа 'long long', то есть:

 #include  

int main(void)
{
int i = -100;
unsigned int u = 190U;
long long a = u + i;
printf("%lld\n", a);
return 0;
}
output:
 90 
Почему тут вывод правильный ( с точки зрения матана)? Типо ну да size long long 8 байт, ну и что?..
3) И ещё вопрос, я тут "поигрался" со значениями 'unsigned int u' и если u сделать "меньше", чем 'int i', то есть:

 #include  

int main(void)
{
int i = -100;
unsigned int u = 90U;
long long a = u + i;
printf("%lld\n", a);
return 0;
}
ну по идее abs(-100) > abs(90)
то вывод будет такой:

 4294967286 
Какая тут логика у всех трех вариантах? Я чет не втыкаю, посмотрел в книжке, потом видос Тимофея Хирьянова и ещё пару видео чет не улавливаю суть.
Твой первый пример выводит мусор в старшем 32-битном слове, если sizeof(long long) == 8, а sizeof(int) == 4 (как практически везде сейчас и есть). Замени %lld на %u, если хочешь печатать unsigned int.

Далее, приведения типов при вычислении выражения не зависят от типа переменной-приёмника (lvalue). Туда потом отдельно всё приводится.

Поэтому во втором выражении дело обстоит так:
 long long a = (long long)(u + (unsigned int)i); 
Операнды суммируются как беззнаковые, переполнение отбрасывается, и ты получаешь свои 90 в 32-битном unsigned int. А потом уже результат приводится к long long.

Третий случай отличается от второго отрицательным значением суммы. Но поскольку она хранится в беззнаковом 32-битном типе int, получается 2 в 32-й степени минус 10. Преобразование в long long идёт без расширения знака (исходный тип же - беззнаковый), так что 4 миллиарда остаются 4-мя миллиардами, а не -10.
Боев Евгений
Боев Евгений
54 053
Лучший ответ
Вова Васюкович чет все равно не понимаю
В первом примере у вас выбран неверный спецификатор типа. У вас %lld, а нужен %u
 #include   
int main(void) {
int i = -100;
unsigned int u = 190U;
int a = u + i;
printf("%u\n", a);
return 0;
}
Далее, в третьем примере...
 #include    
int main(void) {
int i = -100;
unsigned int u = 90U;
// вначале выражение переменная i неявно приведётся к типу unsigned int
// а затем, во время присваивания, неявно приведётся к типу long long int
// что приведёт результат к искажению (потере точности)
long long a = u + i;
// значение -100 типа int, при неявном приведении к типу unsigned int,
// будет приведено к значению 4294967196U
if (-100 == 4294967196U) puts("Yes!");
else puts("No!");
// если использовать спецификатор для типа unsigned long long int
// то получится как раз корректное значение
printf("%llu\n", a);
// потому как 90U + 4294967196U = 4294967286U
if (u + i == 4294967286U) puts("Yes!");
else puts("No!");
// для вывода результата вы выбрали спецификатор типа long long int,
// значение которого неявно приводится к заданному типу без потери точности
printf("%lld\n", a);
if (4294967286LL == 4294967286U) puts("Yes!");
else puts("No!");
return 0;
}
ты забываешь о том, что отрицательные числа представлены дополнительным кодом, а не просто "положительное и 1 в первом бите"Кстати, в первом примере
 int a = u + i;  
printf("%lld\n", a);
при вызове printf нет никакого преобразования - просто ошибка. Ты передаешь int, а говоришь - что long long, поэтому при выводе лишняя память цепляется и ты видишь мусор