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

【C/C++】C语言内存操作与字符串处理汇总

文章目录

  • C语言内存操作与字符串处理
    • 1 内存操作函数
      • 1.1 普通版本
      • 1.2 安全版本
    • 2 字符串函数
      • 2.1 普通版本
      • 2.2 安全版本
    • 3 其他安全替代方案
    • 4 注意事项总结
    • 5 最佳实践

C语言内存操作与字符串处理


C 语言中常见的内存操作和字符串处理函数


1 内存操作函数

1.1 普通版本

  1. memcpy - 内存复制
    原型
#include <string.h>
void* memcpy(void* dest, const void* src, size_t n);

功能:将 src 指向的内存区域的n 个字节复制到 dest 指向的内存区域。
参数

  • dest:目标内存地址。
  • src:源内存地址。
  • n:复制的字节数。
    返回值:返回 dest 的指针。
    注意事项
  • 内存重叠问题:若 srcdest 的内存区域有重叠,行为未定义(需用 memmove 替代)。
  • 目标空间必须足够大,否则导致缓冲区溢出。

示例

int src[] = {1, 2, 3};
int dest[3];
memcpy(dest, src, sizeof(src)); // 复制整个数组

  1. memmove - 安全内存复制
    原型
void* memmove(void* dest, const void* src, size_t n);

功能:与 memcpy 类似,但能正确处理内存重叠问题。
适用场景:当 srcdest 内存区域可能重叠时(如数组元素移位)。

示例

char str[] = "Hello World";
memmove(str + 6, str, 5); // 将前5字节复制到第6字节位置
// 结果:str 变为 "Hello Hello"

  1. memset - 内存填充
    原型
void* memset(void* ptr, int value, size_t n);

功能:将 ptr 指向的内存区域的n 个字节设置为 value(实际按字节填充)。
典型用途

  • 初始化数组为零:memset(arr, 0, sizeof(arr))
  • 设置内存块的特定模式。

注意事项

  • value 的范围应为 0~255(一个字节)。

示例

char buffer[10];
memset(buffer, 'A', 5); // 前5字节填充为 'A'
memset(buffer + 5, 0, 5); // 后5字节填充为0

1.2 安全版本

  1. memcpy_s(C11 标准)
    原型:
#include <string.h> // C11 起支持
errno_t memcpy_s(void* dest, rsize_t dest_size, const void* src, rsize_t src_size);

功能:安全版本的 memcpy,在复制前检查目标缓冲区大小。
参数:

  • dest_size:目标缓冲区的总大小(字节)。
  • src_size:要复制的字节数(必须 ≤ dest_size 且 ≤ src 的实际大小)。
    返回值:
  • 0:成功。
  • 非零:失败(如参数无效或缓冲区溢出风险)。

示例:

char dest[10];
const char* src = "Hello";
errno_t err = memcpy_s(dest, sizeof(dest), src, 6); // 包括 '\0'
if (err != 0) { /* 处理错误 */ }

  1. memset_s(C11 标准)
    原型:
errno_t memset_s(void* dest, rsize_t dest_size, int value, rsize_t count);

功能:安全版本的 memset,检查填充范围是否超出目标缓冲区。
规则:

  • count(填充字节数)必须 ≤ dest_size
  • 若检测到错误(如 dest 为空或 count 过大),可能触发运行时约束处理函数。

示例:

char buffer[10];
errno_t err = memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); // 安全清零

2 字符串函数

2.1 普通版本

  1. strcpy - 字符串复制
    原型
char* strcpy(char* dest, const char* src);

功能:将 src 指向的字符串(含 \0)复制到 dest
注意事项

  • 高危函数:不检查 dest 的空间是否足够,易导致缓冲区溢出。
  • 推荐使用更安全的 strncpysnprintf

示例

char src[] = "Hello";
char dest[10];
strcpy(dest, src); // dest 内容为 "Hello\0"

  1. strncpy - 安全字符串复制
    原型
char* strncpy(char* dest, const char* src, size_t n);

功能:复制 src最多前 n 个字符dest
规则

  • src 长度 >= n:复制前 n 个字符,不添加 \0
  • src 长度 < n:复制全部字符并补 \0n 字节。

示例

char dest[5];
strncpy(dest, "Hello World", sizeof(dest)); 
// dest 内容为 {'H','e','l','l','o'}(无终止符!)

  1. strcat / strncat - 字符串拼接
    原型
char* strcat(char* dest, const char* src); // 危险:不检查长度
char* strncat(char* dest, const char* src, size_t n); // 安全:限制拼接长度

功能:将 src 字符串拼接到 dest 末尾(覆盖 dest\0)。
注意事项

  • dest 必须足够大以容纳拼接后的结果。
  • strncat自动添加终止符

示例

char dest[20] = "Hello";
strncat(dest, " World!", 7); // dest 变为 "Hello World!\0"

  1. strcmp / strncmp - 字符串比较
    原型
int strcmp(const char* s1, const char* s2); // 比较整个字符串
int strncmp(const char* s1, const char* s2, size_t n); // 比较前n个字符

返回值

  • 0:字符串内容相同。
  • 正/负数:根据 ASCII 码差异决定。

示例

if (strcmp("apple", "apple") == 0) { /* 相等 */ }
if (strncmp("apple", "app", 3) == 0) { /* 前3字符相同 */ }

  1. strlen - 字符串长度
    原型
size_t strlen(const char* s);

功能:返回字符串长度(不含 \0)。
注意:若 s 未以 \0 结尾,行为未定义。

示例

int len = strlen("Hello"); // len = 5

  1. strdup - 字符串复制(动态分配)
    原型
char* strdup(const char* s); // 非标准但广泛支持

功能:复制字符串到新分配的内存(需手动 free)。
示例

char* copy = strdup("Hello");
free(copy); // 必须释放内存

2.2 安全版本

  1. strcpy_s(C11 标准)
    原型:
errno_t strcpy_s(char* dest, rsize_t dest_size, const char* src);

功能:安全版本的 strcpy,确保目标缓冲区足够容纳 src(含终止符 \0)。
规则:

  • dest_size 必须大于 strlen(src)
  • dest_size 不足,函数会将 dest[0] 设为 \0 并返回错误。

示例:

char dest[6];
errno_t err = strcpy_s(dest, sizeof(dest), "Hello");
if (err != 0) { /* 目标缓冲区过小 */ }

  1. strncpy_s(C11 标准)
    原型:
errno_t strncpy_s(char* dest, rsize_t dest_size, const char* src, rsize_t count);

功能:安全版本的 strncpy,限制复制的字符数并确保目标缓冲区有效性。
规则:

  • count 需 ≤ dest_size - 1(为终止符预留空间)。
  • src 长度 ≥ count,复制 count 字符并在末尾添加 \0

示例:

char dest[5];
errno_t err = strncpy_s(dest, sizeof(dest), "Hello World", 4); 
// dest 内容为 "Hell\0"

  1. strcat_s(C11 标准)
    原型:
errno_t strcat_s(char* dest, rsize_t dest_size, const char* src);

功能:安全版本的 strcat,确保拼接后字符串不超过目标缓冲区大小。
规则:

  • 剩余空间(dest_size - strlen(dest) - 1)必须 ≥ strlen(src)

示例:

char dest[12] = "Hello"; // 剩余空间为 6
errno_t err = strcat_s(dest, sizeof(dest), " World!");
if (err == 0) { /* 拼接成功 */ }

  1. strnlen(非 C11,但广泛支持)
    原型:
size_t strnlen(const char* s, size_t maxlen);

功能:安全版本的 strlen,限制最大检查长度以防止未终止字符串导致的越界。
返回值:返回字符串长度(不含 \0),若未找到 \0 则返回 maxlen

示例:

char s[10] = "Hello";
size_t len = strnlen(s, sizeof(s)); // len = 5

3 其他安全替代方案

  1. snprintf(格式化字符串安全写入)
    原型:
int snprintf(char* dest, size_t size, const char* format, ...);

功能:将格式化字符串写入 dest,最多写入 size-1 个字符,并自动添加 \0
返回值:成功时返回欲写入的字符数(不含 \0),若空间不足则返回所需字符数(可判断是否需要扩容)。

示例:

char dest[10];
int needed = snprintf(dest, sizeof(dest), "Pi=%.2f", 3.14159);
if (needed >= sizeof(dest)) { /* 缓冲区不足 */ }

  1. 动态内存分配 + strdup(需手动释放)
    原型:
char* strdup(const char* s); // POSIX 标准

功能:复制字符串到新分配的内存(自动计算长度),避免静态缓冲区溢出风险。
注意:需调用 free() 释放内存。

示例:

const char* src = "Dynamic string";
char* dest = strdup(src);
if (dest != NULL) { /* 使用 dest */free(dest); 
}
  • 非标准但实用的安全函数
  1. strlcpy / strlcat(BSD 扩展)
    原型:
size_t strlcpy(char* dest, const char* src, size_t size); // 复制
size_t strlcat(char* dest, const char* src, size_t size); // 拼接

特点:

  • 保证目标字符串以 \0 结尾。
  • 返回源字符串长度,便于判断是否截断。
  • 广泛用于 Linux/BSD,但 Windows 默认不支持。

示例:

char dest[5];
size_t len = strlcpy(dest, "Hello", sizeof(dest)); // len=5, dest="Hell\0"


4 注意事项总结

函数主要风险安全替代方案
strcpy缓冲区溢出strncpy, snprintf
strcat缓冲区溢出strncat
memcpy内存重叠导致未定义行为memmove
gets缓冲区溢出(已废弃)fgets
  • 安全函数的使用原则
  1. 显式指定缓冲区大小:所有安全版本函数均需传递目标缓冲区大小。
  2. 检查返回值:正确处理错误码(如 errno_t 或返回的字符数)。
  3. 避免魔法数值:使用 sizeof(buffer) 而非硬编码长度。
  4. 编译器支持:需启用 C11 标准(如 GCC 使用 -std=c11)。
  5. 跨平台注意:部分安全函数(如 *_s)在非 Windows 环境可能需额外库支持。
场景危险函数安全替代方案
字符串复制strcpystrcpy_s, snprintf
字符串拼接strcatstrcat_s, strlcat
内存复制memcpymemcpy_s, memmove
用户输入读取getsfgets, getline
未初始化内存操作直接访问指针calloc, memset_s

5 最佳实践

  1. 优先使用带长度限制的函数(如 strncpystrncat)。
  2. 检查目标内存大小,避免溢出。
  3. 处理内存重叠时用 memmove
  4. 动态分配内存后记得释放(如 strdup)。

相关文章:

  • 简单实现网页加载进度条
  • Bootstrap 5 容器与网格系统详解
  • Java中的流详解
  • 2025ICPC邀请赛南昌游记
  • 【C语言基础语法入门】通过简单实例快速掌握C语言核心概念
  • 安防综合管理系统EasyCVR视频融合平台安防知识:门禁系统与视频监控系统如何联动?
  • 【Qwen开源】WorldPM: 扩展人类偏好建模
  • EMC基础知识-EFT(上)
  • 基于AI的Web数据管道,使用n8n、Scrapeless和Claude
  • mybatis-plus实操
  • 【Nextcloud】使用 LNMP 架构搭建私有云存储:Nextcloud 实战指南
  • TDesign AI Chat - Vue3.x 可用!腾讯出品的 AIGC 交互对话组件,免费开源、包含设计资源
  • MyBatis入门指南
  • Java微服务架构实战:Spring Boot与Spring Cloud的深度整合
  • 代码审查服务费用受哪些因素影响?如何确定合理报价?
  • React 个人笔记 Hooks编程
  • C#接口的setter或getter的访问性限制
  • 论文阅读--Logical quantum processor based on reconfigurable atom arrays
  • Model 速通系列(一)nanoGPT
  • 智能开发工具PhpStorm v2025.1——增强AI辅助编码功能
  • 钱进已任外交部新闻司副司长
  • 招商基金总经理徐勇因任期届满离任,“老将”钟文岳回归接棒
  • 红星控股重整期间实控人被留置后续:重整草案不会修改,涉车建兴职责已调整
  • 贵州茅台:支持工作餐不上酒的规定,请投资者相信茅台创新和自我调节能力
  • 总书记回信二周年之际,上海如何将垃圾分类深度融入城市发展?
  • “80后”南京大学天文与空间科学学院教授施勇加盟西湖大学