关注

【安全函数】fread_s与fwrite_s的二进制文件安全读写

在C语言开发中,二进制文件读写是处理非文本数据(如图片、结构体、音频)的核心场景,但标准库fread()/fwrite()函数因缺乏严格的参数校验和边界检查,容易引发缓冲区溢出、整数溢出等安全漏洞,成为黑客攻击的潜在入口。为解决这一问题,C11标准正式引入了安全增强版后缀_s函数(如fread_s()fwrite_s()),通过强制参数校验、溢出防护和明确的错误处理,显著提升了文件操作的安全性。


目录

一、安全函数核心认知:为什么需要_s后缀函数?

1.1 标准函数的安全隐患

1.2 _s安全函数的设计目标

1.3 适用场景与行业需求

二、fread_s()函数深度解析

2.1 函数简介

2.2 函数原型与参数详解(C11标准)

2.3 函数实现(伪代码)

2.4 核心使用场景(结合实战)

2.5 注意事项

三、fwrite_s()函数深度解析

3.1 函数简介

3.2 函数原型与参数详解(C11标准)

3.3 函数实现(伪代码)

3.4 核心使用场景(结合实战)

3.5 注意事项(关键避坑)

四、_s安全函数与标准函数(fread/fwrite)核心差异对比

五、常见问题与解决方案

5.1 编译时提示fread_s未定义?

5.2 调用fwrite_s后程序直接终止?

5.3 fread_s返回0,如何区分是“参数错误”还是“文件末尾”?

5.4 跨平台读写结构体时数据错乱?

六、经典面试真题


一、安全函数核心认知:为什么需要_s后缀函数?

1.1 标准函数的安全隐患

C语言标准库的fread()fwrite()设计之初以高效为核心,但缺乏必要的安全检查,存在两大致命缺陷:

  • 参数校验缺失:若传入NULL指针(如缓冲区ptrNULL、文件指针stream未初始化),函数会直接崩溃或产生未定义行为;

  • 边界与溢出风险:当size * nmemb的乘积超过缓冲区实际大小(缓冲区溢出),或乘积超过size_t最大值(整数溢出)时,函数无任何防护,可能导致内存 corruption、恶意代码注入等漏洞;

  • 错误处理模糊:标准函数仅通过返回值和ferror()/feof()判断状态,未明确区分“参数错误”“IO错误”“溢出错误”,排查难度大。

1.2 _s安全函数的设计目标

_s(Secure)系列函数是C11标准(ISO/IEC 9899:2011)引入的“安全增强接口”,核心目标是:

  1. 强制参数合法性校验:对NULL指针、零大小参数、无效文件流等进行严格检查;

  2. 防范整数溢出:检测size * nmemb的乘积是否超出合理范围,避免数值溢出导致的逻辑错误;

  3. 明确错误处理机制:通过errno设置具体错误码,结合“约束处理函数”(constraint handler),让开发者可自定义违反安全约束时的行为(如日志记录、优雅退出);

  4. 兼容原有使用习惯:参数顺序、核心功能与标准函数保持一致,降低迁移成本。

1.3 适用场景与行业需求

_s安全函数特别适用于对安全性要求极高的场景:

  • 嵌入式系统(工业控制、物联网设备):需抵御恶意输入导致的设备失控;

  • 网络编程(二进制协议解析):接收不可信网络数据后写入文件,需防范缓冲区溢出;

  • 金融/医疗软件:处理敏感数据时,需避免因漏洞导致数据泄露或程序崩溃;

  • 遵循安全标准的项目:如符合ISO 26262(汽车安全)、IEC 61508(功能安全)等规范的开发。

二、fread_s()函数深度解析

2.1 函数简介

fread_s()fread()的安全增强版,核心功能仍是从二进制文件流中读取原始字节数据到缓冲区,但在读取前增加了完整的参数校验和溢出检查,读取后提供明确的错误反馈。其设计遵循“安全优先”原则,即使牺牲少量性能,也要避免未定义行为。

2.2 函数原型与参数详解(C11标准)

#include <stdio.h>
size_t fwrite_s(const void *restrict ptr, size_t elementSize, size_t count, FILE *restrict stream);

参数拆解(含安全增强点)

参数名

类型

作用与安全约束

ptr

void *restrict

指向存储读取数据的缓冲区指针,**约束**:不可为NULL,且缓冲区大小≥elementSize * count;<br>restrict限定符:确保ptrstream指向的内存区域不重叠(优化性能+避免数据冲突)。

elementSize

size_t

每个数据项的字节大小(如sizeof(int)),**约束**:不可为0。

count

size_t

计划读取的数据项个数,**约束**:不可为0;且elementSize * count不可超过SIZE_MAX(避免整数溢出)。

stream

FILE *restrict

已打开的文件指针,**约束**:不可为NULL,且需以二进制读取模式("rb")打开。

返回值与错误处理

  • 返回值:成功读取的数据项个数(与标准fread()一致),可能小于count(如到达文件末尾);

  • 错误场景与errno

    • 违反安全约束(如ptrNULLelementSize=0):返回0,errno设为EINVAL,并调用约束处理函数;

    • 整数溢出(elementSize * count > SIZE_MAX):返回0,errno设为EOVERFLOW

    • IO错误(如文件损坏、权限不足):返回0,errno设为EIO,可通过ferror(stream)验证;

    • 文件末尾:返回实际读取个数,feof(stream)返回非0。

2.3 函数实现(伪代码)

fread_s()的核心优势在于“前置校验+过程防护”,以下伪代码模拟其底层实现流程,重点突出安全检查逻辑:

size_t fread_s(void *restrict ptr, size_t elementSize, size_t count, FILE *restrict stream) {
    // -------------- 安全增强:前置参数校验(标准fread无此步骤)--------------
    // 1. 检查核心指针是否为NULL
    if (ptr == NULL || stream == NULL) {
        errno = EINVAL; // 无效参数
        invoke_constraint_handler("fread_s: ptr or stream is NULL"); // 调用约束处理函数
        return 0;
    }
    // 2. 检查数据项大小/个数是否为0
    if (elementSize == 0 || count == 0) {
        errno = EINVAL;
        invoke_constraint_handler("fread_s: elementSize or count is zero");
        return 0;
    }
    // 3. 检查整数溢出(elementSize * count 可能超出SIZE_MAX)
    if (elementSize > SIZE_MAX / count) { // 避免直接相乘溢出
        errno = EOVERFLOW;
        invoke_constraint_handler("fread_s: integer overflow (elementSize * count)");
        return 0;
    }
    size_t total_bytes = elementSize * count; // 安全的总字节数

    // -------------- 核心读取逻辑(与标准fread一致)--------------
    unsigned char *buf = (unsigned char *)ptr;
    size_t bytes_read = 0;
    while (bytes_read < total_bytes) {
        size_t read_now = sys_read(stream->fd, buf + bytes_read, total_bytes - bytes_read);
        if (read_now == 0) break; // 文件末尾或IO中断
        bytes_read += read_now;
    }

    // -------------- 安全增强:后置错误处理(标准fread简化)--------------
    if (bytes_read == 0 && ferror(stream)) {
        errno = EIO;
        invoke_constraint_handler("fread_s: IO error during read");
    }

    // 返回实际读取的数据项个数
    return bytes_read / elementSize;
}
  • 整数溢出防护:通过elementSize > SIZE_MAX / count反向判断,避免elementSize * count直接计算导致的溢出(如SIZE_MAX=4GB时,elementSize=2GBcount=3,直接相乘会溢出为2GB);

  • 约束处理函数invoke_constraint_handler()是C11定义的回调函数,默认行为是调用abort()终止程序,开发者可通过set_constraint_handler_s()自定义(如记录日志后退出)。

2.4 核心使用场景(结合实战)

场景1:安全读取嵌入式传感器的二进制数据

嵌入式设备中,传感器数据以结构体形式存储在Flash中,需安全读取避免缓冲区溢出:

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <constraint_handler.h> // C11约束处理头文件

// 自定义约束处理函数:记录错误日志后终止程序
void my_constraint_handler(const char *msg, void *ptr, errno_t error) {
    fprintf(stderr, "Security Constraint Violation: %s (errno=%d)\n", msg, error);
    abort(); // 或优雅退出:exit(EXIT_FAILURE)
}

// 传感器数据结构体(1字节对齐,跨平台兼容)
#pragma pack(1)
typedef struct {
    uint16_t sensor_id;   // 传感器ID(2字节)
    float temperature;    // 温度(4字节)
    uint32_t timestamp;   // 时间戳(4字节)
} SensorData;
#pragma pack()

// 安全读取传感器数据
SensorData *read_sensor_data(const char *file_path, size_t *read_count) {
    // 设置自定义约束处理函数
    set_constraint_handler_s(my_constraint_handler);

    FILE *fp = fopen(file_path, "rb");
    if (fp == NULL) {
        perror("fopen failed");
        return NULL;
    }

    // 1. 获取文件大小,计算可读取的结构体个数
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    *read_count = file_size / sizeof(SensorData);
    if (*read_count == 0) {
        fclose(fp);
        fprintf(stderr, "No valid sensor data\n");
        return NULL;
    }

    // 2. 分配缓冲区(确保大小足够)
    SensorData *data = (SensorData *)malloc(*read_count * sizeof(SensorData));
    if (data == NULL) {
        fclose(fp);
        perror("malloc failed");
        return NULL;
    }

    // 3. 使用fread_s安全读取
    size_t actual_read = fread_s(data, sizeof(SensorData), *read_count, fp);
    if (actual_read != *read_count) {
        if (errno == EINVAL || errno == EOVERFLOW) {
            // 安全约束违反,已由自定义处理函数记录日志
            free(data);
            fclose(fp);
            return NULL;
        } else if (feof(fp)) {
            fprintf(stderr, "Warning: Reach EOF, actual read %zu items\n", actual_read);
            *read_count = actual_read; // 更新实际读取个数
        } else if (ferror(fp)) {
            perror("fread_s IO error");
            free(data);
            fclose(fp);
            return NULL;
        }
    }

    fclose(fp);
    return data;
}

场景2:分块读取大文件(避免内存溢出)

处理GB级二进制文件时,分块读取是关键,fread_s()的整数溢出检查可避免分块大小计算错误:

#define BUF_SIZE 1024*1024 // 1MB分块缓冲区
int process_large_binary_file(const char *file_path) {
    set_constraint_handler_s(my_constraint_handler);
    FILE *fp = fopen(file_path, "rb");
    if (fp == NULL) return -1;

    unsigned char buf[BUF_SIZE];
    size_t actual_read;

    // 循环分块读取:每次读取1MB,elementSize=1,count=BUF_SIZE
    while ((actual_read = fread_s(buf, 1, BUF_SIZE, fp)) > 0) {
        // 处理当前块数据(如解析、加密、写入其他文件)
        if (process_block(buf, actual_read) != 0) {
            fclose(fp);
            return -1;
        }
    }

    // 检查读取错误
    if (errno != 0 && !feof(fp)) {
        perror("fread_s failed");
        fclose(fp);
        return -1;
    }

    fclose(fp);
    return 0;
}

2.5 注意事项

1. C11兼容性配置

  • GCC:需开启-std=c11-fbound-check编译选项(部分版本需链接-lstdc++);

  • MSVC:默认支持(VS2015及以上),无需额外配置;

  • Clang:需开启-std=c11,部分安全特性需-fsanitize=safe-stack增强检查。

2. 约束处理函数必须配置

  • 若未自定义,默认调用abort()直接终止程序(适合严格安全场景);

  • 自定义时需包含<constraint_handler.h>,函数原型必须符合:void handler(const char *msg, void *ptr, errno_t error)

3. 缓冲区大小必须确保充足

  • fread_s()仅检查elementSize * count是否溢出,不直接检查缓冲区实际大小;

  • 若缓冲区小于elementSize * count,仍可能发生溢出(需开发者自行确保)。

4. 避免混合使用标准函数与_s函数:

  • 同一文件流中,若交替使用fread()fread_s(),需用fflush()fseek()刷新指针,否则可能导致数据错乱。

5. restrict关键字的限制

  • 不可让ptrstream指向重叠内存区域(如用缓冲区同时存储读取数据和文件流控制信息),否则会触发未定义行为。

三、fwrite_s()函数深度解析

3.1 函数简介

fwrite_s()fwrite()的安全增强版,核心功能是将内存中的原始字节数据写入二进制文件流,同样通过前置参数校验、整数溢出防护和明确的错误处理,解决标准函数的安全隐患,尤其适合写入不可信数据或敏感数据。

3.2 函数原型与参数详解(C11标准)

#include <stdio.h>
size_t fwrite_s(const void *restrict ptr, size_t elementSize, size_t count, FILE *restrict stream);

参数拆解(与fread_s()对称)

参数名

类型

作用与安全约束

ptr

const void *restrict

指向待写入数据的缓冲区指针,约束:不可为NULL,数据有效长度≥elementSize * count;<br>const限定:避免写入时修改源数据,restrict确保无指针别名。

elementSize

size_t

每个数据项的字节大小,约束:不可为0。

count

size_t

计划写入的数据项个数,约束:不可为0;且elementSize * countSIZE_MAX(防溢出)。

stream

FILE *restrict

已打开的文件指针,**约束**:不可为NULL,需以二进制写入模式("wb"/"ab")打开。

返回值与错误处理

  • 返回值:成功写入的数据项个数,可能小于count(如磁盘空间不足);

  • 错误场景与errno

    • 违反安全约束(如ptr=NULLelementSize=0):返回0,errno=EINVAL,调用约束处理函数;

    • 整数溢出:返回0,errno=EOVERFLOW

    • IO错误(磁盘满、权限不足):返回0,errno=EIO

    • 写入不完整:返回实际写入个数,需结合ferror(stream)判断是否为错误。

3.3 函数实现(伪代码)

size_t fwrite_s(const void *restrict ptr, size_t elementSize, size_t count, FILE *restrict stream) {
    // -------------- 安全增强:前置参数校验 --------------
    if (ptr == NULL || stream == NULL) {
        errno = EINVAL;
        invoke_constraint_handler("fwrite_s: ptr or stream is NULL");
        return 0;
    }
    if (elementSize == 0 || count == 0) {
        errno = EINVAL;
        invoke_constraint_handler("fwrite_s: elementSize or count is zero");
        return 0;
    }
    // 整数溢出防护
    if (elementSize > SIZE_MAX / count) {
        errno = EOVERFLOW;
        invoke_constraint_handler("fwrite_s: integer overflow (elementSize * count)");
        return 0;
    }
    size_t total_bytes = elementSize * count;

    // -------------- 核心写入逻辑 --------------
    const unsigned char *buf = (const unsigned char *)ptr;
    size_t bytes_written = 0;
    while (bytes_written < total_bytes) {
        size_t write_now = sys_write(stream->fd, buf + bytes_written, total_bytes - bytes_written);
        if (write_now == 0) break; // 写入失败
        bytes_written += write_now;
    }

    // -------------- 安全增强:后置错误处理 --------------
    if (bytes_written == 0 && ferror(stream)) {
        errno = EIO;
        invoke_constraint_handler("fwrite_s: IO error during write");
    }

    return bytes_written / elementSize;
}

3.4 核心使用场景(结合实战)

场景1:安全写入网络二进制数据到本地文件

网络接收的二进制数据(如TCP数据包)可能存在恶意构造的长度,fwrite_s()可防范溢出:

场景2:安全持久化结构体配置数据

将程序配置结构体写入文件时,fwrite_s()的参数校验可避免因结构体指针无效导致的崩溃:

// 应用配置结构体
typedef struct {
    uint32_t app_version;  // 版本号
    uint8_t  log_level;    // 日志级别
    char     server_ip[16];// 服务器IP
} AppConfig;

// 安全保存配置
int save_app_config(const AppConfig *config, const char *config_path) {
    if (config == NULL) {
        fprintf(stderr, "config pointer is NULL\n");
        return -1;
    }

    set_constraint_handler_s(log_constraint_handler);
    FILE *fp = fopen(config_path, "wb");
    if (fp == NULL) return -1;

    // 写入整个结构体,elementSize=sizeof(AppConfig),count=1
    size_t written = fwrite_s(config, sizeof(AppConfig), 1, fp);
    if (written != 1) {
        perror("fwrite_s failed");
        fclose(fp);
        return -1;
    }

    fflush(fp);
    fclose(fp);
    printf("Config saved successfully\n");
    return 0;
}

// 使用示例
AppConfig g_config = {0x01020304, 3, "192.168.1.100"};
save_app_config(&g_config, "app_config.bin");

3.5 注意事项(关键避坑)

1. 文件打开模式必须匹配

  • 写入二进制文件需用"wb"(覆盖)或"ab"(追加),若用文本模式"wt",可能导致换行符转换,破坏二进制数据;

  • 读写模式"rb+"需确保文件已存在,否则fopen()失败。

2. fflush()的强制使用:

  • fwrite_s()仍使用系统缓冲区,数据可能暂存于内存,关键数据写入后需调用fflush(fp)强制刷盘,避免程序异常退出导致数据丢失。

3. 字符串写入的特殊处理

  • C语言字符串以'\0'结尾,若直接写入字符串,需用strlen()获取有效长度(不含'\0'),避免写入多余字节:

    char *str = "binary_data";
    fwrite_s(str, 1, strlen(str), fp); // 正确:写入有效长度

    4. 跨平台兼容性处理

    • 结构体写入前需设置1字节对齐(#pragma pack(1)),避免不同编译器填充字节差异导致读取错乱;

    • 多字节数据(如uint32_t)需统一字节序(大端),跨CPU架构(x86小端/ARM大端)读写时避免解析错误。

    四、_s安全函数与标准函数(fread/fwrite)核心差异对比

    为快速区分两者的适用场景,以下从安全、功能、使用成本等维度进行全面对比:

    对比维度

    fread_s()/fwrite_s()(安全函数)

    fread()/fwrite()(标准函数)

    标准依据

    C11及以上(ISO/IEC 9899:2011)

    C89及以上(传统标准)

    参数校验

    强制校验(NULL指针、零大小、整数溢出),违反约束调用处理函数

    无校验,传入无效参数直接触发未定义行为(崩溃/内存错乱)

    安全防护

    防范整数溢出、隐含缓冲区边界约束(需开发者确保缓冲区大小)

    无任何防护,易引发缓冲区溢出、整数溢出漏洞

    错误处理

    明确的errno错误码(EINVAL/EOVERFLOW/EIO)+ 约束处理机制

    仅通过返回值和ferror()/feof()判断,错误类型模糊

    兼容性

    需C11兼容编译器(GCC/Clang需开启选项,MSVC原生支持)

    所有C编译器兼容,移植性无压力

    性能开销

    少量额外开销(参数校验、溢出检查),对多数场景无影响

    无额外开销,效率最优

    使用成本

    需配置约束处理函数,需检查errno,学习成本略高

    无需额外配置,调用简单,学习成本低

    适用场景

    安全优先(嵌入式、网络、金融),需遵循安全标准的项目

    效率优先,内部系统/可信数据,无安全风险的场景

    指针限定符

    支持restrict,避免指针别名,优化性能

    C99及以上支持restrict,部分老编译器不兼容

    流程对比可视化

    标准函数fread()流程(风险高)

    安全函数fread_s()流程(安全可控)

    五、常见问题与解决方案

    5.1 编译时提示fread_s未定义?

    原因:编译器未开启C11标准,或不支持C11安全函数。

    解决方案

    • GCC/Clang:编译命令添加-std=c11 -fbound-check,部分版本需链接-lstdc++

    • MSVC:VS2015及以上默认支持,若仍报错,在项目属性中开启“C11标准支持”;

    • 若编译器不支持(如老版本GCC),可使用__STDC_WANT_LIB_EXT1__宏启用:

      #define __STDC_WANT_LIB_EXT1__ 1 // 启用C11安全扩展
      #include <stdio.h>

      5.2 调用fwrite_s后程序直接终止?

      原因:违反安全约束(如ptr=NULL),默认约束处理函数调用abort()

      解决方案:自定义约束处理函数,避免程序直接终止:

      size_t actual_read = fread_s(data, sizeof(int), 10, fp);
      if (actual_read == 0) {
          if (errno == EINVAL || errno == EOVERFLOW) {
              printf("参数错误或整数溢出\n");
          } else if (feof(fp)) {
              printf("到达文件末尾\n");
          } else {
              printf("IO错误\n");
          }
      }

      5.3 fread_s返回0,如何区分是“参数错误”还是“文件末尾”?

      解决方案:结合errnofeof()判断:

      #define __STDC_WANT_LIB_EXT1__ 1
      #include <stdio.h>
      #include <constraint_handler.h>
      
      void my_handler(const char *msg, void *ptr, errno_t error) {
          fprintf(stderr, "Constraint Violation: %s\n", msg);
          exit(EXIT_FAILURE); // 优雅退出,而非abort()
      }
      
      int main() {
          set_constraint_handler_s(my_handler); // 注册自定义函数
          // 后续调用fread_s/fwrite_s
      }

      5.4 跨平台读写结构体时数据错乱?

      原因:结构体对齐方式不同,或字节序差异。

      解决方案

      1. 强制1字节对齐:#pragma pack(1)

      2. 使用固定大小数据类型(uint32_t而非int);

      3. 统一字节序(如写入时转大端,读取时转主机序)。

      六、经典面试真题

      面试题1:简述C11标准中fread_sfread的核心差异,以及_s函数的安全增强点。(微软2024年嵌入式工程师面试题)

      答案

      1. 核心差异:

      • 标准依据:fread基于C89,fread_s是C11安全增强接口;

      • 参数校验:fread无校验,fread_s强制校验NULL指针、零大小参数;

      • 安全防护:fread_s新增整数溢出检查,fread无;

      • 错误处理:fread_s通过errno和约束处理函数提供明确反馈,fread错误类型模糊。

      2 安全增强点:

      • 前置参数合法性校验(避免无效参数导致未定义行为);

      • 整数溢出防护(elementSize * count不超过SIZE_MAX);

      • 约束处理机制(自定义违反安全约束时的行为);

      • 支持restrict关键字,避免指针别名优化性能。

      面试题2:C11中_s安全函数的“约束处理机制”是什么?如何自定义约束处理函数?(英特尔2023年系统编程面试题)

      答案

      1. 约束处理机制:是_s函数的核心安全特性,当调用_s函数违反安全约束(如ptr=NULL、整数溢出)时,函数会触发预设的“约束处理函数”,而非直接崩溃或产生未定义行为。

      2. 自定义步骤:

      • 包含头文件<constraint_handler.h>(C11标准头文件);

      • 定义符合原型的处理函数:void handler(const char *msg, void *ptr, errno_t error)

        • msg:约束违反的描述信息;

        • ptr:关联的指针参数(如NULLptr);

        • error:对应的errno错误码;

      3. 调用set_constraint_handler_s(handler)注册自定义函数,覆盖默认行为(默认调用abort())。示例:

      void my_handler(const char *msg, void *ptr, errno_t error) {
          fprintf(stderr, "Security Error: %s (errno=%d)\n", msg, error);
          exit(EXIT_FAILURE);
      }
      set_constraint_handler_s(my_handler);

      面试题3:使用fwrite_s写入二进制文件时,如何避免整数溢出和缓冲区溢出?(牛客网高频题,字节跳动2024年后端开发面试题)

      答案

      1. 避免整数溢出:

      • 利用fwrite_s内置的溢出检查(elementSize > SIZE_MAX / count),无需手动计算乘积;

      • 确保elementSizecount为非零值(fwrite_s会校验,开发者需传递有效参数)。

      2. 避免缓冲区溢出:

      • 确保缓冲区大小≥elementSize * countfwrite_s不直接检查缓冲区大小,需开发者自行保证);

      • 优先使用固定大小缓冲区(如数组),避免动态内存分配时计算错误;

      • 动态分配缓冲区后,通过sizeof或显式记录的大小验证,避免分配过小。

      3. 完整实践步骤:

      • 检查待写入数据的长度,确保elementSize * count合法;

      • 分配足够大的缓冲区并初始化;

      • 调用fwrite_s后检查返回值和errno

      • 自定义约束处理函数,及时捕获约束违反场景。


      fread_s()fwrite_s()作为C11标准的安全增强函数,通过强制参数校验、整数溢出防护和明确的错误处理,解决了传统fread()/fwrite()的安全隐患,是安全敏感场景的首选。开发者在使用时需注意:

      1. 确保编译器支持C11标准,正确配置编译选项;

      2. 自定义约束处理函数,避免程序无故终止;

      3. 严格检查返回值和errno,确保错误可追溯;

      4. 结合结构体对齐、字节序处理,保证跨平台兼容性。

      在安全与效率的权衡中,_s函数并非“银弹”,但在嵌入式、网络、金融等高危场景中,其提供的安全防护能显著降低漏洞风险。随着安全标准的普及,掌握_s系列安全函数已成为C语言开发者的必备技能,也是面试中的高频考点。


      博主简介

      byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发,深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域。乐于技术分享与交流,欢迎关注互动!

      📌 主页与联系方式

      • CSDN:https://blog.csdn.net/weixin_37800531

      • 知乎:https://www.zhihu.com/people/38-72-36-20-51

      • 微信公众号:嵌入式硬核研究所

      • 邮箱[email protected](技术咨询或合作请备注需求)

      ⚠️ 版权声明

      本文为原创内容,未经授权禁止转载。商业合作或内容授权请联系邮箱并备注来意。


      转载自CSDN-专业IT技术社区

      原文链接:https://blog.csdn.net/weixin_37800531/article/details/155110769

      评论

      赞0

      评论列表

      微信小程序
      QQ小程序

      关于作者

      点赞数:0
      关注数:0
      粉丝:0
      文章:0
      关注标签:0
      加入于:--