对函数参数和返回值进行批注
本文介绍简单函数参数(标量以及指向结构和类的指针)以及大多数类型的缓冲区的批注的典型用法。 本文还介绍了批注的常见使用模式。 有关与函数相关的其他批注,请参阅对函数行为进行批注。
指针参数
对于下表中的批注,如果已批注指针参数,当指针为 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);
在此示例中,调用方提供
p1
的size
元素的缓冲区。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)
参数、字段或结果的范围为
low
到hi
(含)。 等效于_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))