当前位置: 首页 > web >正文

【C/C++】Undefined reference: memset_s

Undefined reference: memset_s


memset_s 未定义引用的根本原因

1. 你所使用的 libc 实现并不包含 memset_s

memset_s 是 C11 标准新增的安全函数,但并非所有实现都支持,例如:
环境是否支持 memset_s
GNU libc (glibc)未实现(甚至到 glibc 2.39 都没有)
Windows (MSVC CRT)支持
macOS (libSystem)支持
musl libc不支持
FreeBSD libc支持
编译器附带的内建实现有时支持,有时不支持(依赖平台)

📌 所以如果你在 Linux(glibc)上使用 memset_s,就会链接失败,提示 undefined reference


2. memset_s 是 C11 函数,需显式声明 __STDC_WANT_LIB_EXT1__

即便平台实现了 memset_s,你也要在包含 <string.h> 前定义这个宏:

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>

否则,memset_s 不会出现在头文件中。


3. 你误以为它是内建函数,但没有链接库实现

memset_s 不是编译器内建的,需要由标准库提供实现。如果你使用 gcc 或 clang,但所用平台(如 Ubuntu)用的是 glibc,就不会有它的实现


错误示例(会编译失败):

#include <string.h>int main() {char buffer[100];memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));  // ❌ undefined referencereturn 0;
}

错误信息:

undefined reference to `memset_s'

正确做法(跨平台兼容)

方法一:使用平台安全替代函数

平台安全替代方案
WindowsSecureZeroMemory
Linux (glibc)使用 explicit_bzerobzero
自定义平台自己封装一个安全 memset
#if defined(_WIN32)#include <windows.h>#define secure_memset SecureZeroMemory
#elif defined(__APPLE__) || defined(__FreeBSD__)#include <string.h>#define secure_memset explicit_bzero
#else#include <string.h>static void secure_memset(void* v, int c, size_t n) {volatile unsigned char* p = (volatile unsigned char*)v;while (n--) *p++ = (unsigned char)c;}
#endif

方法二:避免使用 memset_s,自己封装一个兼容版本

static void portable_memset_s(void* dest, size_t destsz, int ch, size_t count) {if (dest == nullptr || count > destsz) {// 标准行为:返回错误码return;}volatile unsigned char* p = (volatile unsigned char*)dest;while (count--) {*p++ = (unsigned char)ch;}
}

总结

问题说明
memset_s 链接失败因为 glibc 没有实现它
需要定义宏#define __STDC_WANT_LIB_EXT1__ 1
安全需求memset 可能被优化掉,memset_s / explicit_bzero 不会
兼容方式自己封装 volatile 写法

explicit_bzero vs memset_s

explicit_bzeromemset_s 都是用于安全清除内存内容的函数,但它们的设计背景、使用语义、跨平台支持以及安全保证略有不同。下面我们从多个角度详细对比:


一览对比表

对比项explicit_bzeromemset_s
所属标准非标准(OpenBSD 起源)
被多个平台采纳
C11 标准扩展 (<string.h>)
是否可用✅ macOS, BSD, Linux(glibc ≥2.25)❌ glibc 不支持
✅ Windows 支持
防优化能力✅ 明确保证不会被优化器移除✅ 由标准要求不可优化
编译要求无需宏定义,直接可用(若平台支持)#define __STDC_WANT_LIB_EXT1__ 1
错误处理无返回值有返回值(错误码)
失败行为没有错误机制(假设参数合法)会检查参数(如 null/越界)并返回错误码
安全级别更适合做“内存清除”任务更适合做“安全 memset”任务
C++ 可用性可用(需要 extern “C” 或封装)可用(但可能链接失败)
可移植性❗较差(部分平台无)❗很差(glibc 无)

详细对比说明

1. 安全语义(防止优化器移除)

函数优化器会移除?保证方式
memset✅ 可能移除
explicit_bzero❌ 不会明确设计为“不可优化”
memset_s❌ 不会标准要求“side-effect observable”

📌 目的相同,但机制不同:explicit_bzero 通常通过 volatile 或外部函数实现,memset_s 要求编译器配合。


2. 返回值行为

  • explicit_bzerovoid,不处理任何错误。

  • memset_s:返回 errno_t,有如下错误码:

    • EINVAL:空指针
    • ERANGE:count 超过 destsz 等等

这意味着:

  • explicit_bzero 是一种更简单的“安全清除”方式;
  • memset_s 更适合被集成在具有防御性编程要求的系统里。

3. 可移植性

explicit_bzero
  • ✅ FreeBSD, OpenBSD, macOS, glibc 2.25+,多数现代 Unix-like 系统支持;
  • ❌ Windows 不支持(需手动实现)。
memset_s
  • ✅ MSVC 实现了(Windows CRT);
  • ❌ GNU glibc 至今 未实现(即使你定义了 __STDC_WANT_LIB_EXT1__ 也没用);
  • ✅ 你可以自己实现,但不如 explicit_bzero 简洁。

4. 使用示例

explicit_bzero
#include <string.h>
char key[32] = "secret";
explicit_bzero(key, sizeof(key));  // 安全清除,无返回值
memset_s
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
char key[32] = "secret";
errno_t err = memset_s(key, sizeof(key), 0, sizeof(key));  // 返回错误码

5. 实现简洁性

  • explicit_bzero 是为“做清除这一个事情”而设计的,不带任何多余检查;
  • memset_s 是标准化、类型安全、检查更多的版本,更加繁琐但更“现代安全”。

实际推荐场景

场景推荐使用
只想清除内存,避免敏感信息泄露explicit_bzero
需要标准库接口,跨平台代码量大封装 secure_memzero()
在 Windows 上写安全代码memset_s ✅(MSVC)或 SecureZeroMemory
项目需对参数错误做出反应memset_s

推荐封装方式

void secure_memzero(void* ptr, size_t len) {if (!ptr || len == 0) return;#if defined(_WIN32)SecureZeroMemory(ptr, len);
#elif defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))explicit_bzero(ptr, len);
#elif defined(__APPLE__) || defined(__FreeBSD__)explicit_bzero(ptr, len);
#elsevolatile unsigned char* p = (volatile unsigned char*)ptr;while (len--) *p++ = 0;
#endif
}

总结

维度explicit_bzeromemset_s
安全✅ 强✅ 强
错误处理❌ 无✅ 有
跨平台❌ 一般❌ 差(glibc 不支持)
简洁性✅ 强❌ 略繁琐
推荐场景✅ 敏感数据清除✅ 高安全、参数可控逻辑
http://www.xdnf.cn/news/16429.html

相关文章:

  • 港股历史逐笔十档分钟级订单簿行情数据分析
  • 黑屏运维OceanBase数据库的常见案例
  • 【算法】前缀和经典例题
  • Kubernetes 监控完全指南:PromQL 通用查询与最佳实践
  • Claude 4.0 终极编程指南:模型对比、API配置与IDE集成实战
  • 深度解析【JVM】三大核心架构:运行时数据区、类加载与垃圾回收机制
  • OGG同步Oracle到Kafka不停库,全量加增量
  • 《汇编语言:基于X86处理器》第9章 编程练习
  • 新房装修是中央空调还是壁挂空调好?
  • 背包DP之完全背包
  • Agentic RAG理解和简易实现
  • UG创建的实体橘黄色实体怎么改颜色?
  • HCIP上HCIA复习静态综合实验
  • 【Java、C、C++、Python】飞机订票系统---文件版本
  • 基于springboot的小区车位租售管理系统
  • dart使用
  • 从入门到进阶:JavaScript 学习之路与实战技巧
  • C++学习笔记(十:类与对象基础)
  • 内存优化:从堆分配到零拷贝的终极重构
  • 【笔记】Handy Multi-Agent Tutorial 第四章: CAMEL框架下的RAG应用 (简介)
  • linux-开机启动流程
  • 蓝桥杯java算法例题
  • NOIP 模拟赛 7
  • ZYNQ芯片,SPI驱动开发自学全解析个人笔记【FPGA】【赛灵思
  • 同声传译新突破!字节跳动发布 Seed LiveInterpret 2.0
  • Win11批量部署神器winget
  • 滚珠导轨:手术机器人与影像设备的精密支撑
  • 升级目标API级别到35,以Android15为目标平台(三 View绑定篇)
  • 上位机程序开发基础介绍
  • Round-Robin仲裁器