Вот прототип функции SystemTimeToTzSpecificLocalTime:
BOOL SystemTimeToTzSpecificLocalTime(
[in, optional] const TIME_ZONE_INFORMATION *lpTimeZoneInformation,
[in] const SYSTEMTIME *lpUniversalTime,
[out] LPSYSTEMTIME lpLocalTime
);
Как видите, второй и третий параметры являются указателями на структуры SYSTEMTIME, поэтому, по крайней мере, с точки зрения статической проверки типов, должна быть возможность передать один и тот же указатель для обоих параметров.
Однако в документации ничего не говорится о том, безопасно это делать или нет.
В ходе тестирования я обнаружил, что передача одного и того же указателя для обоих параметров работает должным образом. Функция изменяет структуру SYSTEMTIME, и вывод правильный.
Вот простой пример:
#include <windows.h>
void main() {
SYSTEMTIME tm;
GetSystemTime(&tm);
SystemTimeToTzSpecificLocalTime(NULL, &tm, &tm);
wprintf(L"Local Time: %02d:%02d:%02d\n", tm.wHour, tm.wMinute, tm.wSecond);
}
Мой вопрос: безопасно ли передавать один и тот же указатель для обоих параметров? Или мне следует использовать новую структуру SYSTEMTIME для вывода?
В документации я не нашел никакой информации об этом. (https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime)
Спасибо!
Предположим, у вас есть более сложный пример, в котором местное время отличается не на целый час. Может ли это привести к тому, что функция допустит часовую ошибку, когда получасовая смена меняет пройденный час?
Учитывая, что входной аргумент равен const, как можно ожидать, что функция сможет производить запись в то же место? Ввод может быть жестко запрограммирован, а не только то, что было прочитано в переменную.





Рэймонд Чен рассказывает об этом в своей книге «Основные правила программирования — параметры функций и способы их использования»
Существуют некоторые основные правила, применимые ко всему системному программированию, настолько очевидные, что большая часть документации не удосуживается объяснить их, поскольку эти правила должны были быть усвоены практиками в данной области до такой степени, что их не нужно выражать. Точно так же, как при прокладке маршрута движения вы даже не подумаете о том, чтобы срезать путь через чей-то задний двор или пойти не в ту сторону по улице с односторонним движением, и точно так же, как опытный шахматист даже не думает о незаконных ходах. Решая, что делать дальше, опытный программист даже не рассматривает возможность нарушения следующих основных правил без явного разрешения в документации на обратное:
Выходные буферы.
- Выходной буфер не может перекрывать входной или другой выходной буфер.
(Помните, что каждое утверждение здесь является основным правилом, а не абсолютным неизбежным фактом. Предположим, что каждое предложение здесь предваряется словами «При отсутствии указаний на обратное». Если вызывающий и вызываемый договорились об исключении из правила, тогда это исключение применимо. Было бы безумием даже рассматривать иное. В результате я уверен, что существуют другие «правила, настолько очевидные, что их не нужно произносить», которые отсутствуют (например, «Вы не можете завершить поток, пока он находится внутри чужой функции». .»)
Одно удобное практическое правило относительно того, что вы можете сделать с вызовом функции, — это спросить: «Как бы мне понравилось, если бы кто-то сделал это со мной?» (Это частный случай теста «Представьте, если бы это было возможно».)
Нет, вы не можете рассчитывать на то, что указатель на один и тот же буфер будет считаться допустимым. Это может сработать. Это не значит, что это действительно. Это не значит, что он не сломается в будущем.
tl;dr: Вот так и будут драконы
См. также несвязанное:
который касается правил языка C, а не правил Windows.
У Рэймонда есть хорошая фраза: а что, если бы кто-то сделал это со мной. Допустим, я реализую SystemTimeToTzSpecificLocalTime.
BOOL SystemTimeToTzSpecificLocalTime(
[in, optional] const TIME_ZONE_INFORMATION *lpTimeZoneInformation,
[in] const SYSTEMTIME *lpUniversalTime,
[out] LPSYSTEMTIME lpLocalTime
)
{
// First things first, we'll zero out the return value.
// Don't want them to encounter random garbage in case something goes sideways.
ZeroMemory(lpLocalTime, sizeof(SYSTEMTIME));
// ...the remainder of the function...
}
Я могу это сделать, потому что я могу установить lpLocalTime, потому что это вся цель моей функции.
Если будет разрешен вход и выход, функция, скорее всего, просто примет один (неконстантный) параметр указанного типа.
Беглый взгляд на документацию, похоже, не запрещает это явно, но зачем рисковать? Структура SYSTEMTIME невелика. Я не вижу в этом никакой выгоды.