C 语言中常见的内存操作和字符串处理函数
memcpy
- 内存复制#include
void* memcpy(void* dest, const void* src, size_t n);
功能:将 src
指向的内存区域的前 n
个字节复制到 dest
指向的内存区域。
参数:
dest
:目标内存地址。src
:源内存地址。n
:复制的字节数。dest
的指针。src
和 dest
的内存区域有重叠,行为未定义(需用 memmove
替代)。示例:
int src[] = {1, 2, 3};
int dest[3];
memcpy(dest, src, sizeof(src)); // 复制整个数组
memmove
- 安全内存复制void* memmove(void* dest, const void* src, size_t n);
功能:与 memcpy
类似,但能正确处理内存重叠问题。
适用场景:当 src
和 dest
内存区域可能重叠时(如数组元素移位)。
示例:
char str[] = "Hello World";
memmove(str + 6, str, 5); // 将前5字节复制到第6字节位置
// 结果:str 变为 "Hello Hello"
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
memcpy_s
(C11 标准)#include // 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
的实际大小)。示例:
char dest[10];
const char* src = "Hello";
errno_t err = memcpy_s(dest, sizeof(dest), src, 6); // 包括 '\0'
if (err != 0) { /* 处理错误 */ }
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)); // 安全清零
strcpy
- 字符串复制char* strcpy(char* dest, const char* src);
功能:将 src
指向的字符串(含 \0
)复制到 dest
。
注意事项:
dest
的空间是否足够,易导致缓冲区溢出。strncpy
或 snprintf
。示例:
char src[] = "Hello";
char dest[10];
strcpy(dest, src); // dest 内容为 "Hello\0"
strncpy
- 安全字符串复制char* strncpy(char* dest, const char* src, size_t n);
功能:复制 src
的最多前 n
个字符到 dest
。
规则:
src
长度 >= n
:复制前 n
个字符,不添加 \0
。src
长度 < n
:复制全部字符并补 \0
至 n
字节。示例:
char dest[5];
strncpy(dest, "Hello World", sizeof(dest));
// dest 内容为 {'H','e','l','l','o'}(无终止符!)
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"
strcmp
/ strncmp
- 字符串比较int strcmp(const char* s1, const char* s2); // 比较整个字符串
int strncmp(const char* s1, const char* s2, size_t n); // 比较前n个字符
返回值:
示例:
if (strcmp("apple", "apple") == 0) { /* 相等 */ }
if (strncmp("apple", "app", 3) == 0) { /* 前3字符相同 */ }
strlen
- 字符串长度size_t strlen(const char* s);
功能:返回字符串长度(不含 \0
)。
注意:若 s
未以 \0
结尾,行为未定义。
示例:
int len = strlen("Hello"); // len = 5
strdup
- 字符串复制(动态分配)char* strdup(const char* s); // 非标准但广泛支持
功能:复制字符串到新分配的内存(需手动 free
)。
示例:
char* copy = strdup("Hello");
free(copy); // 必须释放内存
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) { /* 目标缓冲区过小 */ }
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"
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) { /* 拼接成功 */ }
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
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)) { /* 缓冲区不足 */ }
strdup
(需手动释放)char* strdup(const char* s); // POSIX 标准
功能:复制字符串到新分配的内存(自动计算长度),避免静态缓冲区溢出风险。
注意:需调用 free()
释放内存。
示例:
const char* src = "Dynamic string";
char* dest = strdup(src);
if (dest != NULL) {
/* 使用 dest */
free(dest);
}
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
结尾。示例:
char dest[5];
size_t len = strlcpy(dest, "Hello", sizeof(dest)); // len=5, dest="Hell\0"
函数 | 主要风险 | 安全替代方案 |
---|---|---|
strcpy |
缓冲区溢出 | strncpy , snprintf |
strcat |
缓冲区溢出 | strncat |
memcpy |
内存重叠导致未定义行为 | memmove |
gets |
缓冲区溢出(已废弃) | fgets |
errno_t
或返回的字符数)。sizeof(buffer)
而非硬编码长度。-std=c11
)。*_s
)在非 Windows 环境可能需额外库支持。场景 | 危险函数 | 安全替代方案 |
---|---|---|
字符串复制 | strcpy |
strcpy_s , snprintf |
字符串拼接 | strcat |
strcat_s , strlcat |
内存复制 | memcpy |
memcpy_s , memmove |
用户输入读取 | gets |
fgets , getline |
未初始化内存操作 | 直接访问指针 | calloc , memset_s |
strncpy
、strncat
)。memmove
。strdup
)。