【C/C++】Undefined reference: memset_s

发布于:2025-07-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

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 reference
    return 0;
}

错误信息:

undefined reference to `memset_s'

正确做法(跨平台兼容)

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

平台 安全替代方案
Windows SecureZeroMemory
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_bzero memset_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);
#else
    volatile unsigned char* p = (volatile unsigned char*)ptr;
    while (len--) *p++ = 0;
#endif
}

总结

维度 explicit_bzero memset_s
安全 ✅ 强 ✅ 强
错误处理 ❌ 无 ✅ 有
跨平台 ❌ 一般 ❌ 差(glibc 不支持)
简洁性 ✅ 强 ❌ 略繁琐
推荐场景 ✅ 敏感数据清除 ✅ 高安全、参数可控逻辑

网站公告

今日签到

点亮在社区的每一天
去签到