ZetCode

C memset 函数

最后修改:2025 年 4 月 8 日

内存初始化是 C 编程的基础,而 memset 是将内存块设置为特定值的关键函数。本教程将深入介绍 memset,包括其语法、用法和潜在的陷阱。我们将探讨实际示例,并为关键应用程序讨论更安全的替代方案。了解 memset 有助于在保持程序安全性和可靠性的同时优化内存操作。

什么是 memset?

memset 函数将指定值填充到一块内存中。它声明在 string.h 中,并接受三个参数:指向内存块的指针、填充值以及要设置的字节数。memset 在字节级别操作,使其高效用于批量初始化。对于安全敏感的代码,请考虑 memset_s,它提供了运行时约束检查。

基本 memset 用法

此示例演示了使用 memset 将数组初始化为零。

basic_init.c
#include <stdio.h>
#include <string.h>

int main() {
    char buffer[50];

    // Initialize buffer with zeros
    memset(buffer, 0, sizeof(buffer));

    printf("Buffer contents: ");
    for (size_t i = 0; i < sizeof(buffer); i++) {
        printf("%d ", buffer[i]);
    }
    printf("\n");

    return 0;
}

在此,memsetbuffer 的所有 50 个字节设置为零。sizeof 运算符确保我们初始化整个数组。这是在使用前清除缓冲区的常见模式。请注意,memset 可用于任何内存块,而不仅仅是字符数组。请始终指定正确的尺寸以防止缓冲区溢出。

使用 memset 初始化结构体

memset 可以高效地初始化整个结构体,如下所示。

struct_init.c
#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[20];
    float score;
} Student;

int main() {
    Student s1;

    // Initialize structure to zeros
    memset(&s1, 0, sizeof(Student));

    printf("Initialized Student:\n");
    printf("ID: %d\n", s1.id);
    printf("Name: %s\n", s1.name);
    printf("Score: %.1f\n", s1.score);

    return 0;
}

此示例使用 memset 初始化一个 Student 结构体。sizeof 运算符确保我们将结构体中的所有字节都设置为零。对于大型结构体,此方法比逐字段初始化更快。请注意,这适用于没有指向动态分配内存的指针的简单结构体。填充字节也会被初始化。

设置非零值

此示例演示了将数组的所有元素设置为特定值。

array_init.c
#include <stdio.h>
#include <string.h>

int main() {
    int numbers[10];
    
    // Set all elements to 0xFF
    memset(numbers, 0xFF, sizeof(numbers));

    printf("Array contents: ");
    for (size_t i = 0; i < sizeof(numbers)/sizeof(numbers[0]); i++) {
        printf("%08X ", numbers[i]);
    }
    printf("\n");

    return 0;
}

在此,memset 将整数数组中的所有字节设置为 0xFF。请注意,这会将每个字节(而不是每个数组元素)设置为指定值。对于整数数组,这将导致每个元素都为 0xFFFFFFFF(对于 32 位整数)。此技术对于在内存中创建特定的位模式很有用。在处理非字符数据类型时,请注意字节级别的操作。

安全替代方案:memset_s

此示例演示了 C11 中提供的更安全的 memset_s 函数。

safe_init.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char sensitive[100];
    
    // Secure initialization with bounds checking
    errno_t result = memset_s(sensitive, sizeof(sensitive), 0, sizeof(sensitive));

    if (result != 0) {
        printf("Error: Buffer size mismatch or invalid parameters\n");
        return 1;
    }

    printf("Buffer securely initialized\n");
    return 0;
}

memset_s 增加了边界检查,并在目标大小不正确时返回错误。这有助于防止缓冲区溢出。成功时函数返回零,失败时返回非零。虽然并非普遍可用,但当针对支持边界检查的 C11 或更高标准时,建议用于安全关键型代码。对于清除敏感数据尤其有用。

__STDC_WANT_LIB_EXT1__ 定义为 1,明确表示程序希望使用 C11 标准库的可选扩展。没有这个宏,编译器可能不会提供某些更安全的函数,包括 memset_s。此功能允许开发人员有选择地启用附加功能,以提高程序的安全性和可靠性。

使用 memset 清除字符串

此示例显示了如何使用 memset 清除字符串。

string_clear.c
#include <stdio.h>
#include <string.h>

int main() {
    char password[50] = "MySecretPassword123";
    
    printf("Before clear: %s\n", password);
    
    // Securely clear the password
    memset(password, 0, sizeof(password));
    
    printf("After clear: %s\n", password);

    return 0;
}

在此,memset 通过将所有字节设置为零来安全地清除密码字符串。这对于不应保留在内存中的敏感数据很重要。sizeof 运算符确保我们清除整个缓冲区,而不仅仅是字符串内容。请注意,编译器有时可能会优化掉 memset 调用,因此对于安全性而言,memset_s 更可取。

使用 memset 的最佳实践

来源

C memset 文档

本教程从基本用法到高级注意事项,对 memset 函数进行了探讨。虽然它功能强大,但请始终谨慎使用内存操作,以防止程序中的安全漏洞和未定义行为。

作者

我叫 Jan Bodnar,是一位敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1,400 多篇文章和 8 本电子书分享我的专业知识。凭借十多年的教学经验,我致力于让编程变得易于理解和引人入胜。

列表 C 标准库