Печать нулевых указателей с %p является неопределенным поведением?
это неопределенное поведение для печати нулевых указателей с %p спецификатор преобразования?
#include <stdio.h>
int main(void) {
    void *p = NULL;
    printf("%p", p);
    return 0;
}
вопрос относится к стандарту C, а не к реализациям C.
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_tn задает длину массива для функции, n может иметь нулевое значение при вызове этой функции. Если явно не указано иное в описании конкретной функции в этот раздел, аргументов указатель на такой призыв должны иметь действительные значения, как описано в 7.1.4.
это намеренно?
я не знаю, является ли UB
%pс нулевым указателем на самом деле намеренно, но поскольку стандарт явно заявляет, что нулевые указатели считаются недопустимыми значениями в качестве аргументов для стандартных библиотечных функций, а затем он идет и явно указывает случаи, когда нулевой указатель является допустимым аргументом (snprintf, free и т. д.), а затем он идет и еще раз повторяет требование о том, чтобы аргументы были допустимо даже в нулевых' n ' случаях (memcpy,memmove,memset), то я думаю, что разумно предположить, что комитет по стандартам C не слишком обеспокоен тем, что такие вещи не определены.
авторы стандарта C не прилагали никаких усилий для исчерпывающего перечисления всех поведенческих требований, которым должна соответствовать реализация, чтобы быть подходящей для какой-либо конкретной цели. Вместо этого они ожидали, что люди, пишущие компиляторы, будут проявлять определенное количество здравого смысла, требует ли этого стандарт или нет.
вопрос о том, вызывает ли что-то UB, редко сам по себе полезен. Реальные вопросы важности являются:
должен ли кто-то, кто пытается написать качественный компилятор, заставить его вести себя предсказуемым образом? для описанного сценария ответ однозначно да.
должны ли программисты ожидать, что качественные компиляторы для чего-либо, напоминающего нормальные платформы, будут вести себя предсказуемым образом? в описанном сценарии я бы сказал, что ответ да.
может некоторые тупые авторы компиляторов растягивают интерпретацию стандарта, чтобы оправдать что-то странное? надеюсь, что нет, но не исключаю.
должны ли дезинфицирующие компиляторы кричать о поведении? Это будет зависеть от уровня паранойи их пользователей; очистительный компилятор, вероятно, не должен по умолчанию кричать о таком поведении, но, возможно, предоставить опцию конфигурации, чтобы сделать в случае, если программы могут быть перенесены "умные" / тупые компиляторы, которые ведут себя странно.
Если разумная интерпретация стандарта подразумевает, что поведение определено, но некоторые авторы компилятора растягивают интерпретацию, чтобы оправдать иначе, действительно ли имеет значение, что говорит стандарт?