对函数参数和返回值进行批注

本文介绍简单函数参数(标量以及指向结构和类的指针)以及大多数类型的缓冲区的批注的典型用法。 本文还介绍了批注的常见使用模式。 有关与函数相关的其他批注,请参阅对函数行为进行批注

指针参数

对于下表中的批注,如果已批注指针参数,当指针为 null 时,分析器将报告错误。 此批注适用于指针及其指向的任何数据项。

批注和说明

  • _In_

    对标量、结构、结构指针等类似的输入参数进行批注。 显式可用于简单标量。 该参数必须在预状态中有效,并且不会修改。

  • _Out_

    对标量、结构、结构指针等类似的输出参数进行批注。 不要将此批注应用于无法返回值的对象,例如,由值传递的标量。 参数不必在预状态中有效,但必须在后状态中有效。

  • _Inout_

    对函数将更改的参数进行批注。 它必须同时在前状态和后状态中有效,但假定在调用前后具有不同的值。 必须应用于可修改的值。

  • _In_z_

    指针,指向用作输入的以 null 结尾的字符串。 字符串必须在预状态中有效。 首选具有正确批注的 PSTR 的变体。

  • _Inout_z_

    指针,指向将修改的以 null 结尾的字符数组。 它必须在调用前后均有效,但假定值已更改。 可以移动 null 终止符,但只能访问原始 null 终止符之前的元素。

  • _In_reads_(s)

    _In_reads_bytes_(s)

    数组的指针,由函数读取。 数组的大小为 s 个元素,所有这些元素都必须有效。

    _bytes_ 变体提供的大小以字节为单位,而不是元素数。 仅当大小不能表示为元素时才使用此变体。 例如,仅当使用 wchar_t 的类似函数使用 _bytes_ 变体时,char 字符串才会使用该变体。

  • _In_reads_z_(s)

    指向以 null 结尾且具有已知大小的数组的指针。 null 终止符或 s(如果没有 null 终止符)前的元素必须在预状态中有效。 如果大小(以字节为单位)已知,则按元素大小缩放 s

  • _In_reads_or_z_(s)

    指向以 null 结尾和/或具有已知大小的数组的指针。 null 终止符或 s(如果没有 null 终止符)前的元素必须在预状态中有效。 如果大小(以字节为单位)已知,则按元素大小缩放 s。 (用于 strn 系列。)

  • _Out_writes_(s)

    _Out_writes_bytes_(s)

    指向将由函数编写的 s 元素(字节)数组的指针。 数组元素不必在预状态中有效,并且未指定在后状态中有效的元素数。 如果参数类型上有批注,则会在后状态中应用批注。 例如,考虑下面的代码。

    typedef _Null_terminated_ wchar_t *PWSTR;
    void MyStringCopy(_Out_writes_(size) PWSTR p1, _In_ size_t size, _In_ PWSTR p2);
    

    在此示例中,调用方提供 p1size 元素的缓冲区。 MyStringCopy 使其中一些元素有效。 更重要的是,PWSTR 上的 _Null_terminated_ 批注意味着 p1 在后状态中以 null 结尾。 这样一来,有效元素的数量仍具有明确的定义,但不需要特定的元素计数。

    _bytes_ 变体提供的大小以字节为单位,而不是元素数。 仅当大小不能表示为元素时才使用此变体。 例如,仅当使用 wchar_t 的类似函数使用 _bytes_ 变体时,char 字符串才会使用该变体。

  • _Out_writes_z_(s)

    指向 s 元素的数组的指针。 元素不必在预状态中有效。 在后状态中,null 终止符前的所有元素必须存在且必须有效。 如果大小(以字节为单位)已知,则按元素大小缩放 s

  • _Inout_updates_(s)

    _Inout_updates_bytes_(s)

    指向数组的指针,该数组可由函数读取和写入。 它的大小为 s 个元素,并且在预状态和后状态中均有效。

    _bytes_ 变体提供的大小以字节为单位,而不是元素数。 仅当大小不能表示为元素时才使用此变体。 例如,仅当使用 wchar_t 的类似函数使用 _bytes_ 变体时,char 字符串才会使用该变体。

  • _Inout_updates_z_(s)

    指向以 null 结尾且具有已知大小的数组的指针。 null 终止符前的所有元素必须存在且必须在预状态和后状态中均有效。 后状态中的值假定与预状态中的值不同;包含 null 终止符的位置。 如果大小(以字节为单位)已知,则按元素大小缩放 s

  • _Out_writes_to_(s,c)

    _Out_writes_bytes_to_(s,c)

    _Out_writes_all_(s)

    _Out_writes_bytes_all_(s)

    指向 s 元素的数组的指针。 元素不必在预状态中有效。 在后状态中,第 c 个元素及其之前的元素必须有效。 如果大小已知(以字节为单位而不是元素数),则可以使用 _bytes_ 变体。

    例如:

    void *memcpy(_Out_writes_bytes_all_(s) char *p1, _In_reads_bytes_(s) char *p2, _In_ int s);
    void *wordcpy(_Out_writes_all_(s) DWORD *p1, _In_reads_(s) DWORD *p2, _In_ int s);
    
  • _Inout_updates_to_(s,c)

    _Inout_updates_bytes_to_(s,c)

    指向数组的指针,该数组可由函数读取和写入。 它的大小为 s 个元素,所有这些元素都必须在预状态中有效,并且 c 个元素必须在后状态中有效。

    _bytes_ 变体提供的大小以字节为单位,而不是元素数。 仅当大小不能表示为元素时才使用此变体。 例如,仅当使用 wchar_t 的类似函数使用 _bytes_ 变体时,char 字符串才会使用该变体。

  • _Inout_updates_all_(s)

    _Inout_updates_bytes_all_(s)

    指向数组的指针,该数组大小为 s 个元素且可由函数读取和写入。 定义为等效于:

    _Inout_updates_to_(_Old_(s), _Old_(s)) _Inout_updates_bytes_to_(_Old_(s), _Old_(s))

    换句话说,缓冲区中处于预状态的 s 之前的每个元素在预状态和后状态中都有效。

    _bytes_ 变体提供的大小以字节为单位,而不是元素数。 仅当大小不能表示为元素时才使用此变体。 例如,仅当使用 wchar_t 的类似函数使用 _bytes_ 变体时,char 字符串才会使用该变体。

  • _In_reads_to_ptr_(p)

    指向数组的指针,p - _Curr_(即 p 减去 _Curr_)对于该数组是有效的表达式。 p 前的元素必须在预状态中有效。

    例如:

    int ReadAllElements(_In_reads_to_ptr_(EndOfArray) const int *Array, const int *EndOfArray);
    
  • _In_reads_to_ptr_z_(p)

    指向以 null 结尾的数组的指针,p - _Curr_(即 p 减去 _Curr_)对于该数组是有效的表达式。 p 前的元素必须在预状态中有效。

  • _Out_writes_to_ptr_(p)

    指向数组的指针,p - _Curr_(即 p 减去 _Curr_)对于该数组是有效的表达式。 p 前的元素不必在预状态中有效,但必须在后状态中有效。

  • _Out_writes_to_ptr_z_(p)

    指向以 null 结尾的数组的指针,p - _Curr_(即 p 减去 _Curr_)对于该数组是有效的表达式。 p 前的元素不必在预状态中有效,但必须在后状态中有效。

可选指针参数

当指针参数批注包含 _opt_ 时,它指示该参数可能为 null。 否则,批注行为与不包含 _opt_ 的版本的行为相同。 下面是指针参数批注的 _opt_ 变体的列表:

_In_opt_
_Out_opt_
_Inout_opt_
_In_opt_z_
_Inout_opt_z_
_In_reads_opt_
_In_reads_bytes_opt_
_In_reads_opt_z_

_Out_writes_opt_
_Out_writes_opt_z_
_Inout_updates_opt_
_Inout_updates_bytes_opt_
_Inout_updates_opt_z_
_Out_writes_to_opt_
_Out_writes_bytes_to_opt_
_Out_writes_all_opt_
_Out_writes_bytes_all_opt_

_Inout_updates_to_opt_
_Inout_updates_bytes_to_opt_
_Inout_updates_all_opt_
_Inout_updates_bytes_all_opt_
_In_reads_to_ptr_opt_
_In_reads_to_ptr_opt_z_
_Out_writes_to_ptr_opt_
_Out_writes_to_ptr_opt_z_

输出指针参数

输出指针参数需要特殊表示法来消除参数和指向位置的 null 性的歧义。

批注和说明

  • _Outptr_

    参数不能为 null,并且在后状态中,指向位置不能为 null 且必须有效。

  • _Outptr_opt_

    参数可为 null,但在后状态中,指向位置不能为 null 且必须有效。

  • _Outptr_result_maybenull_

    参数不能为 null,并且在后状态中,指向位置可为 null。

  • _Outptr_opt_result_maybenull_

    参数可为 null,并且在后状态中,指向位置可为 null。

    在下表中,将其他子字符串插入到批注名称中,以进一步限定批注的含义。 各种子字符串包括 _z_COM__buffer__bytebuffer__to_

重要

如果要批注的接口为 COM,请使用这些批注的 COM 形式。 请勿将 COM 批注用于任何其他类型接口。

  • _Outptr_result_z_

    _Outptr_opt_result_z_

    _Outptr_result_maybenull_z_

    _Outptr_opt_result_maybenull_z_

    返回的指针具有 _Null_terminated_ 批注。

  • _COM_Outptr_

    _COM_Outptr_opt_

    _COM_Outptr_result_maybenull_

    _COM_Outptr_opt_result_maybenull_

    返回的指针具有 COM 语义,因此它携带返回指针为 null 的 _On_failure_ 后置条件。

  • _Outptr_result_buffer_(s)

    _Outptr_result_bytebuffer_(s)

    _Outptr_opt_result_buffer_(s)

    _Outptr_opt_result_bytebuffer_(s)

    返回的指针指向大小为 s 个元素或字节的有效缓冲区。

  • _Outptr_result_buffer_to_(s, c)

    _Outptr_result_bytebuffer_to_(s, c)

    _Outptr_opt_result_buffer_to_(s,c)

    _Outptr_opt_result_bytebuffer_to_(s,c)

    返回的指针指向大小为 s 个元素或字节的缓冲区,其中第一个 c 有效。

某些接口约定假定输出参数在失败时为 null。 下表中是首选形式,用于显式 COM 代码时除外。 对于 COM 代码,请使用上一部分中列出的相应 COM 形式。

  • _Result_nullonfailure_

    修改其他批注。 如果函数失败,结果将设置为 null。

  • _Result_zeroonfailure_

    修改其他批注。 如果函数失败,结果将设置为 0。

  • _Outptr_result_nullonfailure_

    如果函数成功,返回的指针将指向有效的缓冲区;如果函数失败,则返回 null。 此批注适用于非可选参数。

  • _Outptr_opt_result_nullonfailure_

    如果函数成功,返回的指针将指向有效的缓冲区;如果函数失败,则返回 null。 此批注适用于可选参数。

  • _Outref_result_nullonfailure_

    如果函数成功,返回的指针将指向有效的缓冲区;如果函数失败,则返回 null。 此批注适用于引用参数。

输出引用参数

引用参数的常见用途是用于输出参数。 对于简单的输出引用参数,例如 int&_Out_,请提供正确的语义。 但是,当输出值是指针(例如 int *&)时,等效的指针批注(如 _Outptr_ int **)不会提供正确的语义。 若要简明地表达指针类型的输出引用参数的语义,请使用以下复合批注:

批注和说明

  • _Outref_

    结果必须在后状态中有效且不能为 null。

  • _Outref_result_maybenull_

    结果必须在后状态中有效,但在后状态中可能为 null。

  • _Outref_result_buffer_(s)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个元素的有效缓冲区。

  • _Outref_result_bytebuffer_(s)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个字节的有效缓冲区。

  • _Outref_result_buffer_to_(s, c)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个元素的缓冲区,其中前 c 个元素有效。

  • _Outref_result_bytebuffer_to_(s, c)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个字节的缓冲区,其中前 c 个字节有效。

  • _Outref_result_buffer_all_(s)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个有效元素的有效缓冲区。

  • _Outref_result_bytebuffer_all_(s)

    结果必须在后状态中有效且不能为 null。 指向包含 s 个字节的有效元素的有效缓冲区。

  • _Outref_result_buffer_maybenull_(s)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个元素的有效缓冲区。

  • _Outref_result_bytebuffer_maybenull_(s)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个字节的有效缓冲区。

  • _Outref_result_buffer_to_maybenull_(s, c)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个元素的缓冲区,其中前 c 个元素有效。

  • _Outref_result_bytebuffer_to_maybenull_(s,c)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个字节的缓冲区,其中前 c 个字节有效。

  • _Outref_result_buffer_all_maybenull_(s)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个有效元素的有效缓冲区。

  • _Outref_result_bytebuffer_all_maybenull_(s)

    结果必须在后状态中有效,但在后状态中可能为 null。 指向包含 s 个字节的有效元素的有效缓冲区。

返回值

函数的返回值类似于 _Out_ 参数,但处于不同取消引用级别,并且无需考虑指向结果的指针的概念。 对于以下批注,返回值为批注对象 - 标量、指向结构的指针或指向缓冲区的指针。 这些批注的语义与相应 _Out_ 批注的语义相同。

_Ret_z_
_Ret_writes_(s)
_Ret_writes_bytes_(s)
_Ret_writes_z_(s)
_Ret_writes_to_(s,c)
_Ret_writes_maybenull_(s)
_Ret_writes_to_maybenull_(s)
_Ret_writes_maybenull_z_(s)

_Ret_maybenull_
_Ret_maybenull_z_
_Ret_null_
_Ret_notnull_
_Ret_writes_bytes_to_
_Ret_writes_bytes_maybenull_
_Ret_writes_bytes_to_maybenull_

格式字符串参数

  • _Printf_format_string_ 指示参数是用于 printf 表达式的格式字符串。

    示例

    int MyPrintF(_Printf_format_string_ const wchar_t* format, ...)
    {
           va_list args;
           va_start(args, format);
           int ret = vwprintf(format, args);
           va_end(args);
           return ret;
    }
    
  • _Scanf_format_string_ 指示参数是用于 scanf 表达式的格式字符串。

    示例

    int MyScanF(_Scanf_format_string_ const wchar_t* format, ...)
    {
           va_list args;
           va_start(args, format);
           int ret = vwscanf(format, args);
           va_end(args);
           return ret;
    }
    
  • _Scanf_s_format_string_ 指示参数是用于 scanf_s 表达式的格式字符串。

    示例

    int MyScanF_s(_Scanf_s_format_string_ const wchar_t* format, ...)
    {
           va_list args;
           va_start(args, format);
           int ret = vwscanf_s(format, args);
           va_end(args);
           return ret;
    }
    

其他常见批注

批注和说明

  • _In_range_(low, hi)

    _Out_range_(low, hi)

    _Ret_range_(low, hi)

    _Deref_in_range_(low, hi)

    _Deref_out_range_(low, hi)

    _Deref_inout_range_(low, hi)

    _Field_range_(low, hi)

    参数、字段或结果的范围为 lowhi(含)。 等效于 _Satisfies_(_Curr_ >= low && _Curr_ <= hi)(适用于批注对象以及相应的预状态或后状态条件)。

    重要

    虽然名称包含“in”和“out”,但 _In__Out_ 的语义不适用于这些批注。

  • _Pre_equal_to_(expr)

    _Post_equal_to_(expr)

    批注值就是 expr。 等效于 _Satisfies_(_Curr_ == expr)(适用于批注对象以及相应的预状态或后状态条件)。

  • _Struct_size_bytes_(size)

    适用于结构或类声明。 指示该类型的有效对象可能大于声明类型的有效对象,字节数由 size 指定。 例如:

    typedef _Struct_size_bytes_(nSize) struct MyStruct { size_t nSize; ... };

    MyStruct * 类型的参数 pM 的缓冲区大小(以字节为单位)则为:

    min(pM->nSize, sizeof(MyStruct))

另请参阅