Неопределенное ли поведение вызывать printf с% s и передавать символ нулевой длины *? - PullRequest
0 голосов
/ 26 июня 2018

Четко ли определена третья строка в следующем коде?

char* result = new char[0];                                                                                                                                                                                                                    
printf("%d\n", strlen(result));                                                                                                                                                                                                                                                        
printf("%s\n", result);                                                                                                                                                                                                                                                                
delete[] result;

Когда я запускаю код, я получаю ожидаемый результат (длина 0, за которой следуют две новые строки). Тем не менее, я не уверен, является ли это четко определенным поведением или мне просто повезло.

Четко ли определен вызов на третьей линии?

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Краткий ответ: Это Неопределенное поведение

Длинный ответ: В C ++ выделение массива размером 0 даст действительный указатель на массив без элементов. Из стандарта (взято из этот ответ ):

Из 5.3.4 / 7

Когда значение выражения в директиве new-new объявлено равным нулю, вызывается функция выделения для выделения массива без элементов.

От 3.7.3.1/2

Эффект разыменования указателя, возвращаемого как запрос нулевого размера, не определен.

(Акцент мой)

Это означает, что нет способа правильно прочитать из (или записать в) указатель, возвращенный из запроса new T[0].

И strlen, и printf для форматирования строки "%s" определены для работы со строками символов, которые заканчиваются специальным символом NUL. Они требуют чтения последовательности символов из предоставленного указателя, чтобы попытаться найти этот NUL символ для правильной работы (что приводит к UB, поскольку для этого требуется разыменование указателя). Это поведение определено в стандарте C, поскольку стандарт C ++ делегирует определения большинства типов / функций библиотеки C обратно стандарту C.

printf доступ для %s определяется следующим образом:

Из стандарта C11 § 7.21.6.1 / 6

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

Символы из массива записываются до (но не включая) завершающего нулевого символа. Если указана точность, записывается не более этого количества байтов. Если точность не указана или превышает размер массива, массив должен содержать нулевой символ.

Для этого требуется доступ к массиву (который будет UB, поскольку указатель недопустим для разыменования)

Бонус

Ваш пример кода фактически вводит UB во второй строке из-за использования strlen, по тем же причинам, что и выше.

strlen определено для выполнения следующих действий:

Из стандарта C11 §7.24.6.3 / 3: Функция strlen

Returns

Функция strlen возвращает количество символов, предшествующих завершающему нулевому символу.

Который является UB по той же причине, что и printf.

0 голосов
/ 26 июня 2018

Извините, что ответили на ваш "оригинальный" вопрос (перед вашим редактированием):

Как насчет C?

В C у вас нет new.

Тем не менее:

strlen считает символы в массиве, пока не будет найден символ NUL.

printf(%s) напечатает символы в массиве до найденного символа NUL.

Если у вас есть собственный компилятор, а массив не содержит символ NUL, две команды продолжат поиск символа NUL после конца массива.

Пример:

char a[6]="Hello ";
char b[100]="world!";
char c[100]="John!";
printf("%s\n",a);

Если компилятор помещает массив b в память непосредственно после массива a, в этом примере будет напечатано «Hello world!».

Однако, если компилятор решит поместить c после a, программа выведет «Hello John!».

Если вы используете компилятор, который может обнаружить доступ вне массива (например, компилятор C ++ для .NET), вы получите ошибку, когда достигнут конец массива, и нет символа NUL или конца массив даже будет обрабатываться так же, как NUL символ.

В целом вы можете сказать: в зависимости от компилятора вы будете вести себя по-разному, когда будете передавать массив в printf(%s), когда он не содержит NUL символа.

Это то, что я бы назвал неопределенным поведением ...

Я не знаю, как ведет себя new char[0] в C ++, но я думаю, что нет никакой разницы с C ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...