Является ли неопределенное поведение печатью нулевых указателей с помощью %p
спецификатора преобразования?
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
Вопрос относится к стандарту C, а не к реализациям C.
c
language-lawyer
c99
undefined-behavior
c11
Дрор К.
источник
источник
Ответы:
Это один из тех странных угловых случаев, когда мы подвержены ограничениям английского языка и несогласованной структуре стандарта. Так что в лучшем случае я могу привести убедительный контраргумент, поскольку доказать это невозможно :) 1
Код в вопросе демонстрирует четко определенное поведение.
Поскольку [7.1.4] является основой вопроса, давайте начнем с этого:
Это корявый язык. Одна интерпретация состоит в том, что элементы в списке являются UB для всех библиотечных функций, если они не отменены отдельными описаниями. Но список начинается с «например», что указывает на то, что он иллюстративный, а не исчерпывающий. Например, в нем не упоминается правильное завершение строк нулевым символом (критично для поведения, например
strcpy
).Таким образом, ясно, что цель / область действия 7.1.4 просто состоит в том, что «недопустимое значение» приводит к UB ( если не указано иное ). Мы должны изучить описание каждой функции, чтобы определить, что считается «недопустимым значением».
Пример 1 -
strcpy
[7.21.2.3] говорит только следующее:
В нем нет явного упоминания нулевых указателей, но и нет упоминания о нулевых терминаторах. Вместо этого из «строки, на которую указывает
s2
» делается вывод, что единственными допустимыми значениями являются строки (то есть указатели на массивы символов с завершающим нулем).Действительно, эту закономерность можно увидеть в отдельных описаниях. Еще несколько примеров:
Пример 2 -
printf
[7.19.6.1] говорит следующее
%p
:Null - допустимое значение указателя, и в этом разделе явно не упоминается, что null - это особый случай, а также то, что указатель должен указывать на объект. Таким образом определяется поведение.
1. Если автор стандартов не явится, или если мы не сможем найти что-то похожее на документ с обоснованием, который проясняет ситуацию.
источник
Краткий ответ
Да . Печать нулевых указателей со
%p
спецификатором преобразования имеет неопределенное поведение. Сказав это, я не знаю ни одной существующей соответствующей реализации, которая могла бы вести себя неправильно.Ответ относится к любому из стандартов C (C89 / C99 / C11).
Длинный ответ
Спецификатор
%p
преобразования ожидает аргумент указателя типа на void, преобразование указателя в печатные символы определяется реализацией. Он не утверждает, что ожидается нулевой указатель.Во введении к функциям стандартной библиотеки говорится, что нулевые указатели в качестве аргументов для функций (стандартной библиотеки) считаются недопустимыми значениями, если явно не указано иное.
C99
/C11
§7.1.4 p1
Примеры для функций (стандартной библиотеки), которые ожидают нулевые указатели как допустимые аргументы:
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
Это намеренно?
Я не знаю, является ли UB
%p
с нулевым указателем на самом деле преднамеренным, но поскольку в стандарте явно указано, что нулевые указатели считаются недопустимыми значениями в качестве аргументов стандартных библиотечных функций, а затем он идет и явно указывает случаи, когда нулевой указатель указатель является действительным аргументом (snprintf, бесплатно, и т.д.), а затем он идет и еще раз повторяет требование аргументы действительными даже в нуле случаев «п» (memcpy
,memmove
,memset
), то я думаю , что это разумно предположить , что Комитет по стандартам C не слишком озабочен тем, чтобы такие вещи не были определены.источник
%p
не должна быть неопределенным поведениемАвторы стандарта C не приложили усилий, чтобы исчерпывающе перечислить все поведенческие требования, которым должна соответствовать реализация, чтобы соответствовать какой-либо конкретной цели. Вместо этого они ожидали, что люди, пишущие компиляторы, будут проявлять определенную долю здравого смысла независимо от того, требует этого Стандарт или нет.
Вопрос о том, вызывает ли что-то UB, редко бывает полезен сам по себе. Настоящие важные вопросы:
Должен ли кто-то, кто пытается написать качественный компилятор, заставить его вести себя предсказуемым образом? Для описанного сценария ответ однозначно положительный.
Должны ли программисты иметь право ожидать, что качественные компиляторы для чего-либо, напоминающего обычные платформы, будут вести себя предсказуемым образом? В описанном сценарии я бы сказал, что да.
Могут ли некоторые тупые авторы компиляторов растянуть интерпретацию Стандарта, чтобы оправдать совершение чего-то странного? Надеюсь, что нет, но не исключаю.
Должны ли дезинфицирующие компиляторы кричать о поведении? Это будет зависеть от уровня паранойи пользователей; дезинфицирующий компилятор, вероятно, не должен по умолчанию кричать о таком поведении, но, возможно, должен предоставить возможность конфигурации на случай, если программы могут быть перенесены на «умные» / глупые компиляторы, которые ведут себя странно.
Если разумная интерпретация Стандарта подразумевает, что поведение определено, но некоторые разработчики компиляторов расширяют интерпретацию, чтобы оправдать иное, имеет ли значение, что говорит Стандарт?
источник
printf("%p", (void*) 0)
неопределенное поведение согласно Стандарту? Вызовы глубоко вложенных функций так же важны для этого, как и цена чая в Китае. И да, UB очень часто встречается в реальных программах - что из этого?%p
каждом возможном значении вопроса.