Что происходит в "?:"? Я понятия не имею о типе возвращаемого значения - PullRequest
0 голосов
/ 06 января 2019

Я думаю, что ((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.

Ответы [ 4 ]

0 голосов
/ 07 января 2019

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

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

0 голосов
/ 06 января 2019

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

0 голосов
/ 06 января 2019

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

unsigned x = (unsigned)-1;

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

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

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

0 голосов
/ 06 января 2019

Тип 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), включите их в тип результата.
  • Если два типа различны, но совместимы (например, массив неизвестного размера и массив известного размера, оба с элементами одного типа), объедините эти два типа. (Другие возможности объединения, помимо размера массива, включают элементы массивов, отличающихся друг от друга, но совместимых типов, функцию с и без списка параметров и параметры функций, являющиеся различными, но совместимыми типами.)
...