Печать нулевых указателей с %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_t
n задает длину массива для функции, n может иметь нулевое значение при вызове этой функции. Если явно не указано иное в описании конкретной функции в этот раздел, аргументов указатель на такой призыв должны иметь действительные значения, как описано в 7.1.4.
это намеренно?
я не знаю, является ли UB
%p
с нулевым указателем на самом деле намеренно, но поскольку стандарт явно заявляет, что нулевые указатели считаются недопустимыми значениями в качестве аргументов для стандартных библиотечных функций, а затем он идет и явно указывает случаи, когда нулевой указатель является допустимым аргументом (snprintf, free и т. д.), а затем он идет и еще раз повторяет требование о том, чтобы аргументы были допустимо даже в нулевых' n ' случаях (memcpy
,memmove
,memset
), то я думаю, что разумно предположить, что комитет по стандартам C не слишком обеспокоен тем, что такие вещи не определены.
авторы стандарта C не прилагали никаких усилий для исчерпывающего перечисления всех поведенческих требований, которым должна соответствовать реализация, чтобы быть подходящей для какой-либо конкретной цели. Вместо этого они ожидали, что люди, пишущие компиляторы, будут проявлять определенное количество здравого смысла, требует ли этого стандарт или нет.
вопрос о том, вызывает ли что-то UB, редко сам по себе полезен. Реальные вопросы важности являются:
должен ли кто-то, кто пытается написать качественный компилятор, заставить его вести себя предсказуемым образом? для описанного сценария ответ однозначно да.
должны ли программисты ожидать, что качественные компиляторы для чего-либо, напоминающего нормальные платформы, будут вести себя предсказуемым образом? в описанном сценарии я бы сказал, что ответ да.
может некоторые тупые авторы компиляторов растягивают интерпретацию стандарта, чтобы оправдать что-то странное? надеюсь, что нет, но не исключаю.
должны ли дезинфицирующие компиляторы кричать о поведении? Это будет зависеть от уровня паранойи их пользователей; очистительный компилятор, вероятно, не должен по умолчанию кричать о таком поведении, но, возможно, предоставить опцию конфигурации, чтобы сделать в случае, если программы могут быть перенесены "умные" / тупые компиляторы, которые ведут себя странно.
Если разумная интерпретация стандарта подразумевает, что поведение определено, но некоторые авторы компилятора растягивают интерпретацию, чтобы оправдать иначе, действительно ли имеет значение, что говорит стандарт?