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

【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.dtcms.com/a/300103.html

相关文章:

  • 【seismic中的颜色】
  • ETF历史高频逐笔分钟数据解析
  • FastAPI入门:demo、路径参数、查询参数
  • Linux进程替换
  • Dynamic Model in RL
  • 渲染篇(二):解密Diff算法:如何用“最少的操作”更新UI
  • 【数据结构】二叉树初阶详解(二):实现逻辑与代码拆解(超详版)
  • 聚类-一种无监督分类算法
  • 中文语音识别与偏误检测系统开发
  • Redis的下载和安装(Linux)
  • 亚马逊全球物流(AGL)与Amazon SEND双重升级:重塑跨境电商物流新生态​
  • FastAPI入门:安装、Pydantic、并发和并行
  • Python应用:三局两胜制石头剪刀布游戏
  • VS Code + LaTeX 绘制电气图完全指南(含 PlantUML 样式参考)
  • 典型的 Vue 3 项目目录结构详解
  • Android中ViewStub和View有什么区别?
  • 过油防溅:3 步搞定 油星乱蹦
  • 具身视觉语言导航算法学习笔记
  • C++20 协程
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-27,(知识点:信号完整性,信号反射,串扰,时延,抖动,衰减)
  • 物联网统一网关:多协议转换与数据处理架构设计
  • useCallback/useMemo
  • Item11:在operator=中处理自我赋值
  • [极客大挑战 2019]FinalSQL--布尔盲注
  • 【web应用】如何进行前后端调试Debug? + 前端JavaScript调试Debug?
  • 内置两大模型,Whisper视频语音转文字,支持批量处理,完全免费!
  • 车载诊断刷写 --- Flash关于擦除和写入大小
  • GStreamer中Element(元素)
  • sendfile系统调用及示例
  • Android 键盘