Обновлено: как насчет того, если бы у нас было это
char value_arr[8];
// value_arr is set to some value
snprintf(value_arr, 8, "%d", *value_arr);
это поведение определяется?
Скажем, по какой-то неуклюжей причине у меня
char value_arr[8];
// value_arr is set to some value
int* value_i = reinterpret_cast<int*>(value_arr);
snprintf(value_arr, 8, "%d", *value_i); // the behaviour in question
Есть ли гарантия, что, например, если *value_i
= 7, то value_arr
примет значение «7». Это поведение определено? Таким образом, value_i
сначала разыменовывается, затем передается по значению, затем форматируется, а затем сохраняется в массиве.
Обычно можно ожидать, что значение *value_i
не изменится, но сохранение строки в value_arr
нарушает это.
Когда я его тестирую, кажется, что он работает должным образом, но я не могу найти окончательного ответа в документации. Сигнатура функции имеет ...
, что, насколько мне известно, имеет какое-то отношение к va_list
, но, боюсь, я не очень разбираюсь в работе вариативных функций.
int sprintf (char* str, const char* format, ... );
Вероятно, это не проблема XY, поскольку я спрашивал в значительной степени из любопытства.
Для исходного кода оценка выражения *value_i
вызывает неопределенное поведение, нарушая правило строгого псевдонима. Нельзя использовать псевдоним массива символов как int
.
Для отредактированного кода snprintf(value_arr, 8, "%d", *value_arr);
это нормально и будет форматировать символьный код первого символа в массиве. Оценка аргументов функции выполняется последовательно - перед входом в функцию. (C++ 17 intro.execution / 11)
Разве сама трансляция не является проблемой из-за различных ограничений выравнивания?
@Deduplicator Если он не выровнен правильно, результат приведения не указан
«Оценка аргументов функции выполняется последовательно - перед входом в функцию». Чтобы уточнить, это только C++ 17?
@ChilliDoughnuts, так было всегда, я просто использовал последний стандарт для ссылки на номера абзацев. В более ранних стандартах последовательность аргументов и постфиксного выражения между собой была менее строгой, но всегда была точка последовательности при входе в функцию.
@ M.M А, хорошо, понятно, значит, snprintf(value_arr, 8, "%s othertext", value_arr)
не будет определяться из ответа @ Karlinde, да? Так как в этом случае он передаст значение указателя.
@ChilliDoughnuts: да, это поведение undefined, поскольку вы записываете новые данные в тот же адрес памяти, из которого читаете данные.
Это неопределенное поведение; Вы используете указатель типа int*
, чтобы указать на объект типа char[8]
с другими / менее строгими требованиями к выравниванию по сравнению с int*
. Затем разыменование этого указателя дает UB.
Следующее можно найти по адресу https://en.cppreference.com/w/cpp/io/c/fprintf:
If a call to sprintf or snprintf causes copying to take place between objects that overlap, the behavior is undefined.
Я бы интерпретировал ваш пример как относящийся к этому случаю, и как таковой, согласно этой странице, он будет классифицирован как неопределенное поведение.
Обновлено: более подробная информация на https://linux.die.net/man/3/snprintf:
Some programs imprudently rely on code such as the following
sprintf(buf, "%s some further text", buf);
to append text to buf. However, the standards explicitly note that the results are undefined if source and destination buffers overlap when calling sprintf(), snprintf(), vsprintf(), and vsnprintf(). Depending on the version of gcc(1) used, and the compiler options employed, calls such as the above will not produce the expected results.
Параметры snprintf
оцениваются перед вызовом, и, поскольку это %d
и *value_i
, это вызов по значению; так что, на мой взгляд, совпадений нет.
Вероятно, проблема XY.