_c语言函数_in参数有何作用?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 参数 正文

__in 并不是 C 语言标准库的一部分,也不是 C 语言本身的关键字,它是一个由 Microsoft 在其 Windows SDK (Software Development Kit) 中定义的、用于增强代码可读性和进行静态分析的宏。

c语言 函数中_in 参数
(图片来源网络,侵删)

__in 是一个文档注释宏,它告诉代码阅读者(以及像 Visual Studio 的静态分析工具 PVS-StudioPrefast)这个参数是一个输入参数


__in 的作用和目的

想象一下一个函数,它接收多个指针参数,这些指针可能指向要读取的数据、要写入的数据,或者两者都有,如果不加任何说明,阅读代码的人需要去查看函数内部实现才能确定每个指针的用途。

__in__out__inout 等宏就是为了解决这个问题而引入的。

__in 的核心作用:

c语言 函数中_in 参数
(图片来源网络,侵删)
  1. 文档化:清晰地表明该参数是一个输入参数,调用者负责确保在调用函数前,该参数指向的数据是有效且准备好的,函数内部只会读取这个指针指向的数据,而不会修改它。
  2. 静态分析:允许编译器或静态分析工具检查代码的正确性,工具可以警告你:
    • 如果一个标记为 __in 的指针参数是 NULL,而函数需要解引用它。
    • 如果调用者试图修改一个标记为 __in 的参数所指向的数据(虽然这本身不会阻止编译,但分析工具会发出警告,因为它违反了 __in 的契约)。

__in 的实际定义

在 Windows SDK 的头文件(如 sal.hsal2.h)中,__in 通常被定义为一个空宏,或者一个带有特定属性(用于 GCC/Clang)的宏。

对于 Visual Studio 的 MSVC 编译器,它的定义通常是这样的:

// 在 sal.h 或类似文件中
#define __in

看到这里你可能会惊讶,它竟然是空的!是的,对于 MSVC __in 主要是一个“标签”或“标记”,编译器在编译时并不会因为这个宏的存在而生成额外的代码,它的价值在于:

  • 代码可读性:程序员能一眼看懂。
  • 静态分析:专门的工具(如 PVS-Studio)会识别这些宏并进行检查。

对于 GCC 和 Clang,它可能被定义为一个编译器属性,

c语言 函数中_in 参数
(图片来源网络,侵删)
// 在 sal.h 或类似文件中
#define __in __attribute__((access(read_only, 1)))

这里的 __attribute__((access(read_only, 1))) 告诉编译器,函数的第一个参数 (1) 是只读的,编译器会据此进行更严格的检查。


使用示例

让我们通过一个例子来理解 __in__out 和它们如何一起工作。

假设我们要编写一个函数,该函数接收一个源字符串和一个目标缓冲区,将源字符串复制到目标缓冲区中。

没有 __in/__out 的版本:

#include <stdio.h>
#include <string.h>
// 不带 SAL 注释的函数
void copy_string(const char* src, char* dest, int dest_size) {
    // 阅读者必须看函数体才能明白 src 是输入,dest 是输出
    if (strlen(src) + 1 > dest_size) {
        printf("Destination buffer is too small!\n");
        return;
    }
    strcpy(dest, src);
}
int main() {
    const char* message = "Hello, World!";
    char buffer[50];
    copy_string(message, buffer, sizeof(buffer));
    printf("Copied string: %s\n", buffer);
    return 0;
}

在这个版本里,你需要读 strcpy(dest, src) 这一行才能明白 src 是输入,dest 是输出。

使用 __in/__out 的版本:

#include <stdio.h>
#include <string.h>
// 在实际项目中,你需要包含相应的头文件,#include <sal.h>
// 为了演示,我们在这里手动定义它们
#ifndef __in
#define __in
#endif
#ifndef __out
#define __out
#endif
#ifndef __inout
#define __inout
#endif
#ifndef __out_ecount
#define __out_ecount(x)
#endif
// 带有 SAL 注释的函数
void copy_string_ex(__in const char* src, __out_ecount(dest_size) char* dest, int dest_size) {
    // 函数签名本身就清晰地说明了参数的用途
    if (strlen(src) + 1 > dest_size) {
        printf("Destination buffer is too small!\n");
        return;
    }
    strcpy(dest, src);
}
int main() {
    const char* message = "Hello, World!";
    char buffer[50];
    copy_string_ex(message, buffer, sizeof(buffer));
    printf("Copied string: %s\n", buffer);
    return 0;
}

代码解读:

  • __in const char* src:
    • __in: 告诉调用者和分析工具,src 是一个输入参数。
    • const: C 语言标准关键字,进一步从语言层面保证了 src 指向的内容不会被修改。__inconst 在这里起到了双重保障的作用。
  • __out_ecount(dest_size) char* dest:
    • __out: 告诉调用者和分析工具,dest 是一个输出参数,调用者不应依赖调用前 dest 中的值,函数将负责写入数据。
    • _ecount(dest_size): 这是一个更具体的 __out 扩展,表示写入到 dest 的数据最多为 dest_size 字节,静态分析工具可以用它来检查缓冲区溢出。
  • int dest_size: 这是一个普通的值参数,用于传递缓冲区大小。

常见的 SAL 宏

除了 __in,Windows SDK 还定义了一系列相关的宏,它们共同构成了 SAL (Source Annotation Language):

全称 描述
__in Input 参数是输入,函数只读取该指针指向的数据。
__out Output 参数是输出,函数会写入该指针指向的数据,调用者不应假设其初始值。
__inout Input/Output 参数既是输入也是输出,函数会先读取,再写入。
__in_z Input, Null-terminated 输入参数,指向以空字符结尾的字符串。
__out_ecount(size) Output, Element Count 输出参数,指向一个缓冲区,函数会写入最多 size 个字节的数据。
__inout_bcount(size) Input/Output, Byte Count 输入/输出参数,指向一个缓冲区,函数会读取和写入最多 size 个字节。
__deref Dereference 用于指针的指针,表示注释应用于解引用后的指针。__in __deref const char* 表示指向一个 const char 的指针,而这个 const char 是输入。

  • __in 是什么? 它是 Microsoft Windows SDK 中的一个,用于源代码注释
  • 它的作用是什么?
    1. 提高可读性:清晰地标明函数参数是输入、输出还是双向的。
    2. 辅助静态分析:帮助代码分析工具(如 PVS-Studio)发现潜在的 bug,如使用 NULL 指针、缓冲区溢出等。
  • 它会影响生成的代码吗? 在 MSVC 中,它本身不生成任何代码,它是一个“元数据”或“标签”。
  • 我应该使用它吗?
    • 如果你正在为 Windows 平台开发,特别是使用 Visual Studio,强烈推荐使用,它能极大地提升代码质量和可维护性。
    • 如果你正在开发跨平台代码(例如在 Linux 上使用 GCC),并且你的编译器支持类似的属性(如 __attribute__),那么使用类似的注释也是一个好习惯。
    • 如果你只是写一些简单的、一次性的脚本,那么不使用它也完全可以。

__in 是一种现代 C/C++ 开发中非常有用的文档和防御性编程工具,它让代码意图更加明确,并能帮助工具自动发现错误。

-- 展开阅读全文 --
头像
红米4x高配版与标准版参数差异在哪?
« 上一篇 今天
Ryzen 5 1600参数有哪些核心性能数据?
下一篇 » 42分钟前

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]