Что происходит в "?:"? Я понятия не имею о типе возвращаемого значения

Я думаю ((1? (int)1: (unsigned int)2) > -1) приводит к 1 (true), но на самом деле это 0 (false) в Visual Studio 2017.

Я думаю, что значение (1? (int)1: (unsigned int)2) должно быть (int)1, потому что 1? верно, и 1 > -1 будет верно.

Я не знаю причину, по которой окончательный результат этого выражения неверен.

Когда я пытаюсь привести как ((int)(1? (int)1: (unsigned int)2) > -1), возвращается 1 (true).

signed int test = -1;
signed int si = 1;
unsigned int ui = 2;

printf("%d\n", ((1 ? si : ui) > test));
return 0;

Я ожидаю, что на выходе будет 1, но фактический результат равен 0.

Ответ 1

Тип a? b: c a? b: c не зависит от a. Это определено безоговорочно типами b и c. Полные правила сложны, но для арифметических операндов тип определяется обычными арифметическими преобразованиями. По сути, два операнда преобразуются в общий тип. Для int и unsigned int результирующий тип будет unsigned int.

Условный оператор ? : ? : описано в п. 6.5.15 стандарта C 2018. В параграфе 4 говорится, что результат "преобразован в тип, описанный ниже".

Параграф 5 описывает результат для арифметических типов, структур и объединений:

Если и второй, и третий операнды имеют арифметический тип, тип результата, который будет определен обычными арифметическими преобразованиями, если бы они применялись к этим двум операндам, является типом результата. Если оба операнда имеют структуру или тип объединения, результат имеет этот тип. Если оба операнда имеют тип void, результат имеет тип void.

Арифметические типы - это целочисленные типы и типы с плавающей точкой, согласно 6.2.5 18. (К ним относятся как действительные, так и сложные типы.) Обычные арифметические преобразования описаны в 6.3.1.8 1, которые (в моем резюме, не указаны):

  • Если какой-либо из них является сложным типом, результат является сложным, а остальные правила описывают тип действительной и мнимой частей. В противном случае результат является реальным, а остальные правила описывают его тип.
  • Если любой из них long double, результат будет long double.
  • В противном случае, если один из них double, результат double.
  • В противном случае, если любой из них является float, результатом будет float.
  • В противном случае целочисленные преобразования применяются к каждому операнду (они указаны в 6.3.1.1 2), а затем оба типа преобразуются в общий целочисленный тип. Полные правила для этого несколько сложны, используют концепцию ранга, которая требует некоторого объяснения, и охватывают некоторые эзотерические ситуации, поэтому я просто суммирую их для нормальных ситуаций: если оба типа являются int или более узкими (имеется в виду меньше битов или одинаковое число) битов, но подписан вместо без знака), результат - int. В противном случае, если оба являются unsigned int или уже, результатом будет unsigned int. В противном случае результат будет более широкого типа.

Правила структуры, объединения и пустоты понятны: два операнда должны иметь одинаковый тип, и это результат.

Параграф 6 описывает результат для указателей:

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

В итоге, это говорит:

  • Если у любого из операндов есть квалификаторы (const, volatile, restrict или _Atomic), _Atomic их в тип результата.
  • Если два типа различны, но совместимы (например, массив неизвестного размера и массив известного размера, оба с одинаковым типом элементов), объедините эти два типа. (Другие возможности объединения, помимо размера массива, включают элементы массивов, отличающихся друг от друга, но совместимых типов, функцию с и без списка параметров, а параметры функций - разные, но совместимые типы.)

Ответ 2

Вы не должны смешивать значения со знаком и без знака, если вы не знаете, что происходит (и вам нужно такое поведение) [ проверьте здесь, почему ]. За кулисами, поскольку в вашем выражении есть число unsigned, C оценивает ваш оператор "больше чем" как unsigned integer >. Следовательно, ваше сравнение не будет оцениваться как true как " unsigned -1 " больше, чем ваш unsigned 1.

Ответ 3

Результат вашего оператора ?: Имеет тип unsigned, так как это общий тип для int и unsigned (ваши 2-й и 3-й операнды). Результат имеет ожидаемое значение 1, но его тип не имеет unsigned.

Остальное никак не связано ?: Это хорошо описано в многочисленных ответах на этот часто задаваемый вопрос: операция сравнения для целых чисел без знака и со знаком

Ответ 4

Просто поставить номер на него, если я урону этот код:

unsigned x = (unsigned)-1;

в программе, которую я сейчас отлаживаю, X имеет значение 4294967295 (UINT_MAX), т.е. ваша программа "видит" сравнение как-то так:

((1 ? (int)1 : (unsigned int)2) > 4294967296)

(Вписал бы это как комментарий, но у меня нет репутации.)