Как программа может определить, определен ли NULL с целым числом или типом указателя? - программирование
Подтвердить что ты не робот

Как программа может определить, определен ли NULL с целым числом или типом указателя?

C разрешает NULL определять любую константу нулевого указателя, другими словами, любое целочисленное выражение константы, которое оценивается в 0, или такое выражение, отлитое от void *. Мой вопрос касается того, действительно ли имеет значение выбор определения, то есть зависит от того, будет ли в противном случае правильная программа зависеть от того, какое определение используется. Для целей этого вопроса я хотел бы игнорировать такие проблемы, как NULL, передаваемый на переменные функции или функции, лишенные прототипов, поскольку я уже рассматривал их отдельно. Предположим sizeof NULL == sizeof(void *) и sizeof NULL == sizeof(T) для некоторого целочисленного типа T, так что sizeof недостаточно, чтобы ответить на вопрос, имеет ли тип NULL тип указателя.

Очевидно, что C11 позволяет различать тип NULL или любое другое выражение: ключевое слово _Generic.

C99 также обеспечивает один неясный способ, который кажется надежным:

int null_has_ptr_type()
{
    char s[1][1+(int)NULL];
    int i = 0;
    return sizeof s[i++], i;
}

Существуют ли какие-либо другие методы, с помощью которых тип NULL может быть определен с помощью соответствующей программы C? Любая работа в C89?

4b9b3361

Ответ 1

Через вопрос, ответы и комментарии, я думаю, мы установили:

  • Путь C11 прост (_Generic).
  • Путь C99 довольно ненадежный из-за ошибок компиляторов.
  • Стриптизирующие подходы являются тупиковыми из-за typedef.
  • Никаких других подходов не найдено.

Таким образом, ответ кажется, что нет надежного метода pre-C11 и, по-видимому, никакого действительного метода pre-C99.

Ответ 2

Получите определение строки NULL, а затем выполните полную проверку, как хотите. Вот очень простой подход:

#define XSTR(x) #x
#define STR(x) XSTR(x)

if (STR(NULL)[0] == '(') {
   ...
}

Но я не знаю, как вы справитесь с __null, который может выйти из этого.

Ответ 3

Не могли бы вы подкрепить макрос и посмотреть на строку?

# include <stddef.h>
# include <stdio.h>
# include <string.h>

# define STRINGIFY(x) STRINGIFY_AUX(x)
# define STRINGIFY_AUX(x) #x

int main(void)
{
  const char *NULL_MACRO = STRINGIFY(NULL);

  if (strstr("void", NULL_MACRO) != NULL)
    puts("pointer");
  else
    puts("integer");
}

Он правильно печатает "integer", если вы добавляете это (обычно NULL имеет тип pinter):

# undef NULL
# define NULL 0

NULL не может быть чем-то вроде (int) ((void *) 0), поскольку стандарты не указывают, что константа нулевого указателя, преобразованная в целочисленный тип, по-прежнему является константой нулевого указателя.

Кроме того, стандарт также говорит об этом с целыми константными выражениями (C11, 6.6/6):

Целочисленное константное выражение 117) должно иметь целочисленный тип и должно иметь только операнды, которые являются целыми константами, константами перечисления, символьными константами, выражениями sizeof, результаты которых целочисленные константы, выражения _Alignof и плавающие константы, которые являются непосредственными операндами приведения. Операторы Cast в целочисленном постоянном выражении должны преобразовывать только арифметические типы в целые типы, за исключением того, что они являются частью операнда для оператора sizeof или _Alignof.

EDIT: на самом деле это не работает с такими вещами, как:

# define NULL (sizeof(void *) - sizeof(void *))

(спасибо, что заметили), и это невозможно проверить тривиально, как требуется OP, требуется небольшая работа (простой синтаксический анализ).

РЕДАКТИРОВАТЬ 2: и есть typedef, как правильно указал комментарий.

Ответ 4

Вот один неясный способ: если программа использует выражение &*NULL, это не будет компилироваться с NULL с целым типом, но будет, если NULL имеет тип указателя.

C99 имеет формулировку для этого частного случая:

Если операнд [оператора &] является результатом унарного *оператора, ни тот оператор, ни оператор & не оцениваются и результат, как если бы оба были опущены, за исключением того, что ограничения на операторы все еще применяются, и результат не является lvalue.

Ограничения на операторы не нарушаются: операнд & является результатом унарного оператора *, а операнд унарного оператора * имеет тип указателя (поскольку мы предполагаем NULL определяется таким образом).