Печать нулевых указателей с %p является неопределенным поведением?


это неопределенное поведение для печати нулевых указателей с %p спецификатор преобразования?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}

вопрос относится к стандарту C, а не к реализациям C.

3 92

3 ответа:

это один из тех странных угловых случаев, когда мы подвержены ограничениям английского языка и непоследовательной структуре в стандарте. Так что в лучшем случае я могу сделать убедительный контраргумент, так как это невозможно доказать это :)1


код в вопросе демонстрирует четко определенное поведение.

как [7.1.4] это основа вопроса, Давайте начнем там:

каждый из следующих операторов применяется, если явно не указано иное в следующих подробных описаниях: если аргумент функции имеет недопустимое значение (например значение вне области определения функции или указатель вне адресного пространства программы или нулевой указатель,[... остальные примеры. ..])[...] поведение не определено. [... другие высказывания ...]

это неуклюжий язык. Одна из интерпретаций заключается в том, что элементы в списке являются UB для всех библиотечных функций, если они не переопределены отдельными описаниями. Но список начинается с "например", указывая, что это иллюстративный, а не исчерпывающий. Например, он не упоминает правильное нулевое завершение строк (критическое для поведения, например,strcpy).

таким образом, ясно, что цель / область действия 7.1.4-это просто " недопустимый значение " приводит к UB (если не указано иначе). Мы должны посмотреть на описание каждой функции, чтобы определить, что считается "недопустимым значением".

Пример 1.strcpy

[7.21.2.3] говорит только это:

The strcpy функция копирует строку, на которую указывает s2 (включая завершающий нулевой символ) в массиве, на который указывает s1. Если копирование происходит между перекрывающимися объектами, то поведение не определено.

он не делает явного упоминания о нулевых указателях, но он также не упоминает о нулевых Терминаторах. Вместо этого можно сделать вывод из "строки, на которую указывает s2 " что единственными допустимыми значениями являются строки (т. е. указатели на массивы символов с нулевым завершением).

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

  • [7.6.4.1 (fenv)] храните текущую среду с плавающей запятой в

Короткий Ответ:

да. Печать нулевых указателей с помощью %p спецификатор преобразования имеет неопределенное поведение. Сказав это, я не знаю о какой-либо существующей соответствующей реализации, которая будет плохо себя вести.

ответ относится к любому из стандартов C (C89/C99/C11).


Длинный Ответ

The %p описатель преобразования ожидает аргумент типа указатель на void, преобразование указатель на печатаемые символы определяется реализацией. Он не указывает, что ожидается нулевой указатель.

во введении к стандартным библиотечным функциям говорится, что нулевые указатели в качестве аргументов к (стандартным библиотечным) функциям считаются недопустимыми значениями, если явно не указано иное.

C99/C11§7.1.4 p1

[...] Если аргумент функции имеет неверное значение (например, [...] пустой указатель, [...] поведение не определено.

примеры для функций (стандартная библиотека), которые ожидают указатели null в качестве допустимых аргументов:

  • fflush() использует нулевой указатель для промывки "все потоки" (которые применяются).
  • freopen() использует нулевой указатель для указания файла, "связанного в данный момент" с потоком.
  • snprintf() позволяет передавать нулевой указатель, когда n-это ноль.
  • realloc() использует указатель для выделение нового объекта.
  • free() позволяет передать нулевой указатель.
  • strtok() использует нулевой указатель для последующих вызовов.

если мы возьмем случай для snprintf(), имеет смысл разрешить передачу нулевого указателя, когда 'n' равно нулю, но это не относится к другим (стандартным библиотечным) функциям, которые позволяют аналогично нулю 'n'. Например: memcpy(),memmove(),strncpy(),memset(),memcmp().

это не только указано в введение в стандартную библиотеку, но и еще раз во Введение к этим функциям:

C99 §7.21.1 p2/C11 §7.24.1 p2

где аргумент объявлен как size_t n задает длину массива для функции, n может иметь нулевое значение при вызове этой функции. Если явно не указано иное в описании конкретной функции в этот раздел, аргументов указатель на такой призыв должны иметь действительные значения, как описано в 7.1.4.


это намеренно?

я не знаю, является ли UB %p с нулевым указателем на самом деле намеренно, но поскольку стандарт явно заявляет, что нулевые указатели считаются недопустимыми значениями в качестве аргументов для стандартных библиотечных функций, а затем он идет и явно указывает случаи, когда нулевой указатель является допустимым аргументом (snprintf, free и т. д.), а затем он идет и еще раз повторяет требование о том, чтобы аргументы были допустимо даже в нулевых' n ' случаях (memcpy,memmove,memset), то я думаю, что разумно предположить, что комитет по стандартам C не слишком обеспокоен тем, что такие вещи не определены.

авторы стандарта C не прилагали никаких усилий для исчерпывающего перечисления всех поведенческих требований, которым должна соответствовать реализация, чтобы быть подходящей для какой-либо конкретной цели. Вместо этого они ожидали, что люди, пишущие компиляторы, будут проявлять определенное количество здравого смысла, требует ли этого стандарт или нет.

вопрос о том, вызывает ли что-то UB, редко сам по себе полезен. Реальные вопросы важности являются:

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

  2. должны ли программисты ожидать, что качественные компиляторы для чего-либо, напоминающего нормальные платформы, будут вести себя предсказуемым образом? в описанном сценарии я бы сказал, что ответ да.

  3. может некоторые тупые авторы компиляторов растягивают интерпретацию стандарта, чтобы оправдать что-то странное? надеюсь, что нет, но не исключаю.

  4. должны ли дезинфицирующие компиляторы кричать о поведении? Это будет зависеть от уровня паранойи их пользователей; очистительный компилятор, вероятно, не должен по умолчанию кричать о таком поведении, но, возможно, предоставить опцию конфигурации, чтобы сделать в случае, если программы могут быть перенесены "умные" / тупые компиляторы, которые ведут себя странно.

Если разумная интерпретация стандарта подразумевает, что поведение определено, но некоторые авторы компилятора растягивают интерпретацию, чтобы оправдать иначе, действительно ли имеет значение, что говорит стандарт?