M i c r o s o f t Microsoft Microsoft 源代码注释语言 S A L SAL SAL
c
/**
* @file
* @author jUicE_g2R(qq:3406291309)------------彬(bin-必应)
* 通信与信息专业大二在读
*
* @brief Microsoft 源代码注释语言 SAL
*
* @copyright 2023.10
* @COPYRIGHT 原创技术笔记:转载需获得博主本人同意,且需标明转载源
*
* @language C/C++
*
* @IDE Base on Microsoft Visual Studio 2022
*/
jUicE_g2R的个人主页
文章目录
- [M i c r o s o f t Microsoft Microsoft 源代码注释语言 S A L SAL SAL](#M i c r o s o f t Microsoft Microsoft 源代码注释语言 S A L SAL SAL)
-
- jUicE_g2R的个人主页
- [[快速传送到 Microsoft 官方教程](https://learn.microsoft.com/zh-cn/cpp/code-quality/understanding-sal?view=msvc-170)](#快速传送到 Microsoft 官方教程)
- [1 定义](#1 定义)
- [2 简述作用](#2 简述作用)
-
- [2-1 举个栗子](#2-1 举个栗子)
- [2-2 S A L SAL SAL 的检错能力](#2-2 S A L SAL SAL 的检错能力)
- [3 四种基本类型的参数 ------ 对 形参 形参 形参 的注释](#3 四种基本类型的参数 —— 对 形参 形参 形参 的注释)
-
- [3-1 参数注释的区别](#3-1 参数注释的区别)
- [3-2 Outptr](#3-2 Outptr)
- [4 对 形参 形参 形参 的注释](#4 对 形参 形参 形参 的注释)
- [5 哪些情况添加 S A L SAL SAL 是必要](#5 哪些情况添加 S A L SAL SAL 是必要)
快速传送到 Microsoft 官方教程
1 定义
M i c r o s o f t Microsoft Microsoft 源代码注释语言 ( S A L SAL SAL ) 提供一组描述函数如何使用其参数 的注释,有关注释的假设以及完成注释时的保证。 注释是在头文件 <sal.h>
中定义的。
c
#include <sal.h>
同时它也叫:代码安全增强 与 标准注解 语言
2 简述作用
M i c r o s o f t Microsoft Microsoft 教程原话是:
1) S A L SAL SAL 是让编译器为你检查代码的一种低成本的方法
2)使用 S A L SAL SAL 注释减少代码缺陷
其实就是提高 D e b u g Debug Debug 的效率!!!
其次,使用了 S A L SAL SAL 相当于给代码后面添加的注释一样,只不过面向不是其他人,而是编译器( M i c r o s o f t Microsoft Microsoft 教程原话是:编译器无法读取文档或非正式注释)
2-1 举个栗子
memcpy
将源缓冲区中的 count
个字节复制到目标缓冲区:
不使用 S A L SAL SAL
c
void * memcpy(
void *dest,
const void *src,
size_t count
);
此时编译器不知道传入的参数有什么作用。
使用 S A L SAL SAL
c
void * memcpy(
_Out_writes_bytes_all_(count) void *dest, //参数是只读的,不进行修改
_In_reads_bytes_(count) const void *src, //参数是只写的
size_t count
);
不懂继续看下面的表
2-2 S A L SAL SAL 的检错能力
c
_Out_writes_bytes_all_(count) //count向编译器注明了参数的空间大小
( c o u n t ) (count) (count) 注明了空间范围,这对编译器检查 空间溢出 空间溢出 空间溢出 这种 B u g Bug Bug 的效率无疑大大提高了。
空间溢出 空间溢出 空间溢出 的问题可能存在于 数组在遍历时读过头了 ,正式点叫:差一错误(BUG: O F F − B Y − O N E OFF-BY-ONE OFF−BY−ONE ERROR)
3 四种基本类型的参数 ------ 对 形参 形参 形参 的注释
类别 | 参数注释 | 说明 |
---|---|---|
输入到调用的函数 | _In_ |
数据传递给调用的函数,并被视为只读。 |
输入到调用的函数并输出到调用方 | _Inout_ |
可用数据传递到函数中,并可能进行修改。 |
输出到调用方 | _Out_ |
调用方只为调用的函数执行写入操作提供空间。 调用的函数将数据写入该空间。 |
指针输出到调用方 | _Outptr_ |
与"输出到调用方"一样。 调用的函数返回的值是一个指针。 |
下表显示了如何区分必需参数和可选参数:
参数为必需参数 | 参数为可选参数 | |
---|---|---|
输入到调用的函数 | _In_ |
_In_opt_ |
输入到调用的函数并输出到调用方 | _Inout_ |
_Inout_opt_ |
输出到调用方 | _Out_ |
_Out_opt_ |
指针输出到调用方 | _Outptr_ |
_Outptr_opt_ |
这些注释帮助以正式且准确的方式识别可能的未初始化值和无效空指针的使用。 将 NULL 传递给必需参数可能会导致崩溃,或者可能会导致返回"失败"错误代码。 无论哪种情况,函数都无法成功完成其工作。
3-1 参数注释的区别
_In_ |
_In_opt_ |
|
---|---|---|
可否修改 | 否 | 否 |
可否接受空指针 | 否 | 可 |
_Out_
与 _Out_opt_
,_Inout_
与 _Inout_opt_
,_Outptr_
与 _Outptr_opt_
的关于空指针( N U L L NULL NULL)的接受性区别同理
3-2 Outptr
面向 指针 指针 指针 的注释,以 _Outptr_
不可接受空指针的范例展示其应用
c
void GoodOutPtrCallee(_Outptr_ int **pInt)
{
int *pInt2 = new int; //为(局部)指针的指向对象分配内存空间
*pInt2 = 5; //给(局部)指针的指向对象赋值
*pInt = pInt2; //赋地址,pInt也指向pInt2的指向对象
}
void BadOutPtrCallee(_Outptr_ int **pInt)
{
int *pInt2 = new int;
// Did not initialize pInt buffer before returning!
*pInt = pInt2;
}
void OutPtrCaller(void)
{
int *pInt = NULL; //pInt:地址变量
GoodOutPtrCallee(&pInt); //传入指针的地址
BadOutPtrCallee(&pInt);
}
4 对 形参 形参 形参 的注释
可以对 函数 的 返回值 返回值 返回值 r e t v a l retval retval 进行注释
典型的栗子: r e t v a l retval retval 的类型是 b o o l bool bool 型的
_Success_
注释与 _Out_
组合:
c
_Success_(return != false) // Can also be stated as _Success_(return)
bool GetValue(_Out_ int *pInt, bool flag)
{
if(flag) {
*pInt = 5;
return true;
} else {
return false;
}
}
当它为 t r u e true true 时,指示函数已成功
5 哪些情况添加 S A L SAL SAL 是必要
- 所有指针参数
- 提供值范围注释(如数组,容器一类)