Путаница в тернарном операторе и типизации


Я прошел через этот вопрос -

Почему результат: 1 ? (int *)0 : (void *)0
отличается к результату : 1 ? (int *)0 : (void *)1

Чем это отличается ? Это должно быть 0 или (int*)0.
Как проверить результат ?
где мы можем использовать такой тип выражения ?

2 6

2 ответа:

Единственное различие заключается в типах: первый возвращает an int *, второй возвращает a void *.

Из стандарта 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  )));