memset 函数补充

在C语言中,memset 是一个非常常用的函数,用于将一块内存区域填充为指定的值。它特别适合用来初始化数组或清零内存。以下是对 memset 的详细解释,包括它的用法和参数含义。

memset 的定义

memset 声明在 <string.h> 头文件中,函数原型如下:

void *memset(void *ptr, int value, size_t num);
  • 返回值:返回指向填充后内存区域的指针(即 ptr)。
  • 功能:将从 ptr 开始的 num 个字节设置为 value 的值。

参数含义

  1. void *ptr

    • 指向要填充的内存区域的指针。可以是任何类型的指针(如 char *uint8_t * 等),因为 void * 是通用的。
    • 通常是你要初始化的数组或变量的地址。
  2. int value

    • 指定填充的值,但这个值会被截断为一个字节(0 到 255),因为 memset 是按字节填充的。
    • 虽然参数类型是 int,但实际只使用低 8 位(即 value & 0xFF)。
    • 例如,如果你传入 0x41(ASCII 中的 'A'),每个字节会被填充为 0x41
  3. size_t num

    • 要填充的字节数,类型是 size_t(无符号整数)。
    • 表示从 ptr 开始的连续 num 个字节会被设置为 value

使用示例

示例 1:将数组清零
#include <stdio.h>
#include <string.h>

int main() {
    char arr[10];
    memset(arr, 0, sizeof(arr)); // 将 arr 的 10 个字节设置为 0

    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
  • 输出0 0 0 0 0 0 0 0 0 0
  • 解释
    • arr 是一个 10 字节的字符数组。
    • memset(arr, 0, sizeof(arr)) 将整个数组填充为 0。
    • sizeof(arr) 计算数组的总字节数(这里是 10)。
示例 2:填充特定值
#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main() {
    uint8_t password_data[3];
    memset(password_data, 'A', 3); // 将 3 个字节填充为 'A' (ASCII 0x41)

    for (int i = 0; i < 3; i++) {
        printf("%c ", password_data[i]);
    }
    printf("\n");
    return 0;
}
  • 输出A A A
  • 解释
    • password_data 是一个 3 字节的 uint8_t 数组。
    • memset(password_data, 'A', 3) 将每个字节设置为 'A' 的 ASCII 值(0x41)。
示例 3:部分填充
#include <stdio.h>
#include <string.h>

int main() {
    char arr[5] = "Hello";
    memset(arr, 'x', 2); // 只填充前 2 个字节

    printf("%s\n", arr);
    return 0;
}
  • 输出xxllo
  • 解释
    • 原数组是 "Hello"(内容为 H e l l o \0)。
    • memset(arr, 'x', 2) 将前 2 个字节设置为 'x',结果变成 x x l l o \0

注意事项

  1. 按字节填充

    • memset 是按字节操作的,不能直接用来填充多字节类型(如 intfloat)的非零值。
    • 例如,memset(arr, 1, sizeof(arr)) 会将每个字节设置为 0x01,而不是将整个 int 设置为 1。
    int arr[3];
    memset(arr, 1, sizeof(arr)); // 不推荐这样用
    // arr[0] 会变成 0x01010101,而不是 1
    
  2. 范围控制

    • num 必须小于或等于目标内存区域的大小,否则会导致缓冲区溢出,引发未定义行为。
  3. 常见用途

    • 清零数组或结构体:memset(ptr, 0, size)
    • 初始化为特定字节值(如 'A'0xFF 等)。

uint8_t 的结合

对于您之前的例子 uint8_t password_data[3] = {'0', '1', '2'},可以用 memset 重置或初始化:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main() {
    uint8_t password_data[3];
    memset(password_data, '0', 3); // 填充为 '0' '0' '0'

    printf("%c%c%c\n", password_data[0], password_data[1], password_data[2]);
    return 0;
}
  • 输出000
  • 解释:将 3 个字节都设置为 '0'(ASCII 48)。

总结

  • 用法memset(指针, 值, 字节数)
  • 参数
    • ptr:目标内存地址。
    • value:要填充的字节值(截取低 8 位)。
    • num:填充的字节数。
  • 特点:简单高效,适合清零或填充单字节值。

在C语言中,memset 也可以用来初始化或填充结构体,它的用法和填充数组类似,因为结构体在内存中也是一块连续的区域。以下是关于如何使用 memset 处理结构体的详细说明。

memset 回顾

void *memset(void *ptr, int value, size_t num);
  • ptr:指向结构体的指针。
  • value:填充的字节值。
  • num:填充的字节数,通常是结构体的总大小(通过 sizeof 计算)。

使用 memset 初始化结构体

结构体是由多个成员组成的复合类型,memset 可以将结构体的所有字节设置为某个值(通常是 0 来清零)。

示例 1:清零结构体
#include <stdio.h>
#include <string.h>

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

int main() {
    Student s1;
    memset(&s1, 0, sizeof(s1)); // 将整个结构体清零

    printf("ID: %d, Name: %s, Score: %.2f\n", s1.id, s1.name, s1.score);
    return 0;
}
  • 输出ID: 0, Name: , Score: 0.00
  • 解释
    • sizeof(s1) 计算结构体 Student 的大小(例如,假设 int 是 4 字节,char[10] 是 10 字节,float 是 4 字节,总共 18 字节,实际大小可能因内存对齐而变化)。
    • memset(&s1, 0, sizeof(s1))s1 的所有字节设置为 0:
      • id 变为 0,
      • name 变为全 0(空字符串,因为第一个字节是 '\0'),
      • score 变为 0.0。
示例 2:填充特定值
#include <stdio.h>
#include <string.h>

typedef struct {
    char a;
    char b;
    char c;
} Test;

int main() {
    Test t1;
    memset(&t1, 'A', sizeof(t1)); // 将结构体填充为 'A'

    printf("a: %c, b: %c, c: %c\n", t1.a, t1.b, t1.c);
    return 0;
}
  • 输出a: A, b: A, c: A
  • 解释
    • Test 结构体大小是 3 字节(假设无对齐填充)。
    • memset(&t1, 'A', sizeof(t1)) 将所有字节设置为 'A'(ASCII 0x41)。

注意事项

  1. 内存对齐

    • 结构体的实际大小可能因编译器的内存对齐规则而大于成员的直接总和。例如:
      typedef struct {
          char a;  // 1 字节
          int b;   // 4 字节
      } Example;
      
      • sizeof(Example) 可能是 8 字节(因为 int 需要 4 字节对齐,可能有 3 字节填充)。
      • memset 会填充所有字节,包括填充字节。
  2. 非零填充的局限性

    • memset 按字节填充,因此只能将每个字节设置为同一个值。如果想让结构体的 intfloat 成员设置为非零值(例如 1),memset 不适合,因为它无法直接设置多字节类型的值。
    • 例如:
      memset(&s1, 1, sizeof(s1)); // 不推荐
      
      • 这会将每个字节设置为 0x01,导致 s1.id 变成 0x01010101(16843009),而不是 1。
  3. 推荐用法

    • 清零memset 是清零结构体的最佳选择(value = 0)。
    • 非零初始化:手动赋值或使用初始化列表:
      Student s1 = {1, "Alice", 95.5}; // 推荐
      
示例 3:结合 uint8_t 和结构体

假设您有一个包含 uint8_t 数组的结构体:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

typedef struct {
    uint8_t password_data[3];
    int flag;
} Data;

int main() {
    Data d1;
    memset(&d1, 0, sizeof(d1)); // 清零整个结构体

    printf("Password: %c%c%c, Flag: %d\n", d1.password_data[0], d1.password_data[1], d1.password_data[2], d1.flag);

    // 填充 password_data 为 '0'
    memset(d1.password_data, '0', sizeof(d1.password_data));
    printf("Password: %c%c%c, Flag: %d\n", d1.password_data[0], d1.password_data[1], d1.password_data[2], d1.flag);
    return 0;
}
  • 输出
    Password: , Flag: 0
    Password: 000, Flag: 0
    
  • 解释
    • 第一次 memset(&d1, 0, sizeof(d1)) 清零整个结构体。
    • 第二次 memset(d1.password_data, '0', sizeof(d1.password_data)) 只填充 password_data 数组为 '0'

结论

  • 用法memset(&struct_var, value, sizeof(struct_var))
  • 清零:非常适合用 memset,例如 memset(&struct_var, 0, sizeof(struct_var))
  • 填充特定字节值:可以,但仅限于单字节值(如 'A'0xFF)。
  • 注意:如果需要设置多字节成员(如 intfloat)为特定非零值,应手动赋值而非依赖 memset

`memset` 是 C/C++ 中用于快速初始化一块内存区域的一个函数,通常被用来将一段连续的内存空间设置成相同的值。然而,在某些特定场景下 `memset` 的效率可能会显得较低。 ### 原因分析 1. **功能限制** - `memset` 只能对内存填充固定值(如0、-1等)。如果需要更复杂的数据初始化操作(例如数组元素分别赋不同的初值),则无法直接通过 `memset` 完成,必须借助循环或其他手段补充完成。这种额外的操作会降低整体性能。 2. **数据类型适配问题** - 对于非字符型的基本数据类型的变量或结构体成员来说,使用 `memset` 进行清零或者其他简单值的设定有可能引发意想不到的问题。比如对于浮点数或是联合体内存布局特殊的处理不当容易导致未定义行为。 3. **现代编译器优化差异** - 随着硬件指令集的发展以及高级别语言特性的增加,简单的 memset 操作可能不如一些更为现代化的技术高效。例如 SIMD(Single Instruction Multiple Data 单指令多数据)技术能够并行地处理大量数据;而传统的 memset 往往基于单字节单位逐一写入,虽然很多编译器会对它做一定程度上的优化,但在极致追求性能时仍显不足。 4. **缓存命中率的影响** - 当初始化大规模内存块的时候,由于CPU Cache的工作原理,大片连续访问内存可能导致cache失效现象加剧,从而使程序运行速度变慢。此时采用分段小批量的方式利用局部性原则或许可以改善情况,但这显然不是单纯调用一次 memset 能解决的事情了。 ### 总结 尽管如此,在多数日常应用场合里,合理使用的 `memset` 依然是足够高效的,并且因为它简洁明快的特点深受开发者喜爱。但如果处于极端性能敏感环境中,则需结合实际情况考虑是否替换其他更适合当前任务需求的办法来提升效能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值