Является ли strlen в строке с неинициализированными значениями неопределенным поведением?

strlen возвращает количество символов, предшествующих завершающему нулевому символу. Реализация strlen может выглядеть так:

size_t strlen(const char * str)
{
    const char *s;
    for (s = str; *s; ++s) {}
    return(s - str);
}

Эта конкретная реализация разыменовывает s, где s может содержать неопределенные значения. Это эквивалентно этому:

int a;
int* p = &a;
*p;

Так, например, если бы кто-то сделал это (что приводит к тому, что strlen дает неправильный вывод):

char buffer[10];
buffer[9] = '\0';
strlen(buffer); 

Это неопределенное поведение?


person ペニス    schedule 12.09.2014    source источник
comment
@ user2864740 вы уверены, что строка должна содержать какое-то значение? Разве C не разрешено счастливо падать при чтении перед записью?   -  person kay    schedule 12.09.2014


Ответы (4)


Вызов стандартной функции strlen вызывает неопределенное поведение. DR 451 поясняет это:

библиотечные функции будут демонстрировать неопределенное поведение при использовании с неопределенными значениями.

Более подробное обсуждение см. .

person M.M    schedule 12.09.2014
comment
Мой комментарий ниже относится к реализации постером функции strlen. Согласитесь, что стандартная библиотека имеет другие ограничения или свободы. - person KC-NH; 12.09.2014
comment
@KC-NH обновил мой пост, чтобы уточнить, что я говорю о стандартной функции strlen, а не о псевдореализации OP. - person M.M; 12.09.2014
comment
DR и ответ комитетов на него не являются нормативными, и в данном конкретном случае вы цитируете вещи вне контекста. Фраза, которую вы цитируете, является ответом на вопрос о том, может ли передача неопределенных значений в библиотеку иметь неопределенное поведение. DR, который вы цитируете, на самом деле показывает, что вопрос относительно сложен и приводит к таким простым ответам, как этот. - person Jens Gustedt; 12.09.2014
comment
@JensGustedt хорошо, мы могли бы сказать, что стандарт неясен, но DR 451 представляет мнение комитета по этому вопросу. Я не думаю, что цитата вырвана из контекста; но кто сомневается может и должен прочитать DR 451 полностью - person M.M; 12.09.2014
comment
@MattMcNabb, я не в этом. Я думаю, что указание на DR здесь в значительной степени неуместно, поскольку речь идет о стабильности неуказанных значений. Представленный здесь код считывает каждый байт только один раз, поэтому стабильность не является проблемой. См. мой ответ для углубленного анализа кода в том виде, в котором он представлен, здесь. - person Jens Gustedt; 12.09.2014
comment
@JensGustedt мой ответ касается char buffer[10]; buffer[9] = '\0'; strlen(buffer); , где strlen относится к стандартной библиотечной функции strlen. Я думал, что мое первое предложение ясно дало понять - person M.M; 12.09.2014
comment
Тогда вы не рассматриваете вопрос, который задан подробно. И даже тогда цитирование ответа на вопрос 3 дефектного отчета без ссылки на тот же вопрос 3 вводит в заблуждение. Он дает ответ, может ли это привести к НБ, а не во всех ли случаях. - person Jens Gustedt; 12.09.2014
comment
@JensGustedt Заданный вопрос: это неопределенное поведение? Определение собственной функции с именем strlen приводит к неопределенному поведению, поэтому в любой интерпретации это UB. Но моя интерпретация текста OP заключается в том, что он спрашивает о поведении стандартной функции strlen. Он опубликовал свой собственный псевдокод в качестве обоснования того, почему задал вопрос: он подозревает, что стандартная функция strlen может получить доступ к неопределенным значениям, чтобы найти длину строки. - person M.M; 12.09.2014

Поведение варианта, который вы показываете, хорошо определено в этих обстоятельствах.

  • Все байты неинициализированного массива имеют неопределенные значения, за исключением 10-го элемента, для которого вы установили значение 0.
  • Доступ к неопределенному значению будет UB только в том случае, если адрес базового объекта никогда не будет взят или если значение является ловушкой для соответствующего типа.
  • Поскольку это массив, а доступ к элементам массива осуществляется с помощью арифметики указателей, первый случай здесь не важен.
  • К любому значению char можно получить доступ без UB, пункты о представлениях ловушки в стандарте явно исключают из этого все типы символов.
  • Таким образом, значения, с которыми вы имеете дело, просто «не указаны».
  • Чтение неуказанных значений может, по мнению некоторых членов комитета по стандартам C, каждый раз давать разные результаты, что некоторые называют «полным» состоянием или около того. Это свойство здесь не имеет значения, так как ваша функция считывает любое такое значение не более одного раза.
  • Таким образом, ваш доступ к элементам массива дает вам любое произвольное, но допустимое значение char.
  • Вы уверены, что ваш цикл for останавливается самое позднее в позиции 9, поэтому вы не переполните свой массив.

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

person Jens Gustedt    schedule 12.09.2014

Нет, это не неопределенное поведение. Ваша функция strlen остановится до конца буфера. Если ваша функция strlen ссылается на буфер [10], тогда да, это не определено.

Это, безусловно, будет неожиданным поведением, так как большая часть буфера содержит случайные данные. «Неопределенный» — специальное слово для людей, пишущих языковые стандарты. Это означает, что могло случиться что угодно, включая сбои памяти или выход из программы. Под неожиданным я подразумеваю, что это определенно не то, чего хотел программист. В некоторых прогонах результатом strlen может быть 3 или 10.

person KC-NH    schedule 12.09.2014

Да, это неопределенное поведение. Из проекта стандарта C11, §J.2 Неопределенное поведение:

Поведение не определено в следующих обстоятельствах:

...

Значение объекта с автоматическим сроком хранения используется, пока оно неопределенно.

person nneonneo    schedule 12.09.2014
comment
Этот код на самом деле не использует неопределенные значения (buffer не является неопределенным, но buffer[0] является). Однако strlen использует значения. Кроме того, это приложение не является нормативным (предполагается, что оно должно быть своего рода указателем для поиска различных случаев UB). Нормативный текст является более подробным и имеет некоторые исключения для случаев, когда неопределенное использование не является UB. - person M.M; 12.09.2014
comment
Объект не только неопределен, но и значения просто не указаны, так что ничего плохого произойти не может. - person Jens Gustedt; 12.09.2014