Перекрытие памяти с помощью sprintf (snprintf)

Обновлено: как насчет того, если бы у нас было это

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.

user2100815 27.10.2018 00:37

Вероятно, это не проблема XY, поскольку я спрашивал в значительной степени из любопытства.

ChilliDoughnuts 27.10.2018 00:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
1 512
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Для исходного кода оценка выражения *value_i вызывает неопределенное поведение, нарушая правило строгого псевдонима. Нельзя использовать псевдоним массива символов как int.

Для отредактированного кода snprintf(value_arr, 8, "%d", *value_arr); это нормально и будет форматировать символьный код первого символа в массиве. Оценка аргументов функции выполняется последовательно - перед входом в функцию. (C++ 17 intro.execution / 11)

Разве сама трансляция не является проблемой из-за различных ограничений выравнивания?

Deduplicator 27.10.2018 00:36

@Deduplicator Если он не выровнен правильно, результат приведения не указан

M.M 27.10.2018 00:39

«Оценка аргументов функции выполняется последовательно - перед входом в функцию». Чтобы уточнить, это только C++ 17?

ChilliDoughnuts 27.10.2018 00:49

@ChilliDoughnuts, так было всегда, я просто использовал последний стандарт для ссылки на номера абзацев. В более ранних стандартах последовательность аргументов и постфиксного выражения между собой была менее строгой, но всегда была точка последовательности при входе в функцию.

M.M 27.10.2018 00:50

@ M.M А, хорошо, понятно, значит, snprintf(value_arr, 8, "%s othertext", value_arr) не будет определяться из ответа @ Karlinde, да? Так как в этом случае он передаст значение указателя.

ChilliDoughnuts 27.10.2018 00:56

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

Remy Lebeau 27.10.2018 01:58

Это неопределенное поведение; Вы используете указатель типа 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, это вызов по значению; так что, на мой взгляд, совпадений нет.

Stephan Lechner 27.10.2018 00:45

Другие вопросы по теме