Путаница в тернарном операторе и типизации
Я прошел через этот вопрос -
Почему результат: 1 ? (int *)0 : (void *)0
отличается к результату :
1 ? (int *)0 : (void *)1
Чем это отличается ? Это должно быть 0
или (int*)0
.
Как проверить результат ?
где мы можем использовать такой тип выражения ?
2 ответа:
Единственное различие заключается в типах: первый возвращает an
int *
, второй возвращает avoid *
.Из стандарта C11, §6.5.15 условный оператор, ¶6:
Если и второй, и третий операнды являются указателями или один является нулевой константой указателя, а другой-указателем, то результирующий тип является указателем на тип, содержащий все квалификаторы типов, на которые ссылаются оба операнда. Кроме того, если оба операнда являются указателями на совместимые типы или по-разному квалифицированные версии совместимых типов, Тип результата является указателем на соответствующим образом квалифицированную версию составного типа; если один операнд является константой нулевого указателя, результат имеет тип другого операнда ; В противном случае один операнд является указателем на
void
или квалифицированная версияvoid
, В этом случае тип результата является указателем на соответствующую квалифицированную версиюvoid
.(курсив наш мой)
Помните, что указатель на не -void
не может быть нулевой константой указателя, а просто нулевым указателем. C11 §6.3.2.3 Указатели, ¶3:Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу
void *
, называется константой нулевого указателя .66) Если константа нулевого указателя преобразуется в тип указателя, то результирующий указатель, называемый нулевым указателем , гарантированно сравните неравенство с указателем на любой объект или функцию.66) макрос
NULL
определяется в<stddef.h>
(и другие заголовки) как константа нулевого указателя; см. 7.19.Итак, вот:
1 ? (int *) 0 : (void *) 0
(int *) 0
является просто нулевым указателем , в то время как(void *) 0
является нулевой константой указателя , поэтому результат имеет типint *
("если один операнд является константой нулевого указателя, то результат имеет тип другого операнда. операнд ").Пока здесь:
1 ? (int *) 0 : (void *) 1
Нет нулевых констант указателя (только нулевой указатель, первый), поэтому результат имеет составной тип
void *
("если оба операнда являются указателями на совместимые типы или на различные версии совместимых типов, то результирующий тип является указателем на соответствующую версию составного типа ").Результаты имеют разные типы, но они оба нулевые указатели . Также обратите внимание, что результат никогда
0
как вы говорите в своем вопросе, это всегда указатель.К сожалению, нет стандартного способа увидеть разницу в C, C++ имеет некоторую поддержку (
typeinfo
), но результаты там разные.Я не могу думать о полезном и конкретном использовании этого темного уголка языка.Где мы можем использовать такой тип выражения?
Итак, effeffe уже ответил на вопрос, почему эти два выражения различаются:
1 ? (int *) 0 : (void *) 0 // yield (int *) 0 1 ? (int *) 0 : (void *) 1 // yield (void *) 0
Чтобы ответить на этот вопрос сейчас:
Как проверить результат ?
С помощью
gcc
можно использовать расширениеtypeof
и встроенную функцию__builtin_types_compatible_p
:// The two expressions differ printf("%d\n", __builtin_types_compatible_p(typeof(1 ? (int *) 0 : (void *) 0), typeof(1 ? (int *) 0 : (void *) 1))); // First expression yield (int *) 0 printf("%d\n", __builtin_types_compatible_p(typeof((int *) 0), typeof( 0 ? (int *)0 : (void *)0 ))); // Second expression yield (void *) 0 printf("%d\n", __builtin_types_compatible_p(typeof((void *) 0), typeof( 1 ? (int *)0 : (void *)1 )));