Undefined behavior – это все, что явно не указано в документации

Навеяно постом про ExUuidCreate и в частности вот этой фразой:

Well, I suggest MSFT to documented this behavior, or at least explain this case in documentation.

Вкратце, суть статьи сводится к тому, что функция ExUuidCreate может изменять содержимое возвращаемого буфера даже в том случае, если она возвращает ошибку. Далее, в комментариях, завязался спор на тему имеет ли функция право трогать буфер в случае ошибки.

Для начала небольшое объяснение почему эта функция имеет право делать с буфером всё что угодно. ExUuidCreate объявлена следующим образом:

NTSTATUS 
ExUuidCreate(
    OUT UUID *Uuid,
    );

Т.е. она пишет сгенерированный GUID в буфер, выделенный вызывающей стороной. Тут важно, что параметр объявлен как “OUT” параметр. Посмотрим, что говорит документация:

__out: The function will only write to the buffer. If used on the return value or with _deref, the function will provide the buffer and initialize it. Otherwise, the caller must provide the buffer, and the function will initialize it.

__out (это тоже самое, что и OUT), указывает на то, что:

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

  2. Вызывающая сторона отвечает за выделение буфера.

Ни слова о состоянии буфера в случае успешного или неуспешного вызова. Но, состояние буфера в случае успешного выполнения описано в документации на саму функцию:

Uuid: Pointer to a caller-allocated UUID (GUID) structure that is set to a new UUID value.

Далее нам понадобиться немного дедукции. Фактически у нас осталось два случая:

  1. Функция возвращает ошибку, содержимое буфера не изменилось;

  2. Функция возвращает ошибку, содержимое буфера изменилось.

Если считать, что функция не должна изменять содержимое буфера в случае ошибки, то где-то в документации должно быть соответствующее требование. Однако же, такого требования там нет, соответственно правомочны оба варианта. Вывод - состояние “OUT” параметра неопределенно в случае неуспешного вызова.

Какой из этого следует вывод? Мне кажется, он достаточно очевиден: всё, что явно не описано в документации - не определено. В свете этого, можно определить критерии полноты документации: документация полна, если все задуманные аспекты поведения функции (модуля, класса и т.д.) описаны. Если документация описывает детали конкретной реализации, то такая документация избыточна и несколько вредна, так как усложняет внесение изменений в код в будущем.

comments powered by Disqus