C语言实现字符串部分复制及常用拷贝函数详解

陆景行
陆景行
发布时间:2026-10-15 | 4人浏览过
原创

在 C 语言编程中,经常遇到需要将字符串从特定索引位置开始复制到新缓冲区的需求。本文将首先演示如何通过自定义函数实现这一功能,并指出手动实现时关于字符串结束符 '\0' 的关键陷阱。随后,文章将深入对比剖析 strcpy、memcpy、strncpy 及 memmove 四个核心拷贝函数的底层逻辑与适用边界,特别是针对内存区域重叠情况的处理差异,为实际开发提供准确的选型依据。

第一部分:手动实现字符串部分复制

本题要求编写一个函数,将输入字符串 t 中从第 m 个字符(索引通常从 1 开始计,代码实现需注意偏移)开始的所有字符复制到字符串 s 中。若 m 超过字符串长度,结果应为空串。

函数接口定义:

void strmcpy( char *t, int m, char *s );

准备条件与核心逻辑分析

由于题目环境未包含 <string.h>,我们需要先自行实现一个计算字符串长度的辅助函数,或者直接在逻辑中处理。这里采用内置逻辑来避免额外依赖。

步骤 1:处理越界情况

首先检查起始位置 m 是否超出了源字符串 t 的实际长度。如果 m 大于或等于长度(考虑到从 1 开始计数,且需保留空间给 '\0'),则直接返回,目标数组 s 应保持为空。

步骤 2:逐字符复制与终止符处理

这是最容易出错的地方。如果使用简单的 for 循环复制字符,往往忽略了 C 语言字符串必须以 '\0' 结尾的特性。如果忘记添加终止符,使用 printf("%s", s) 输出时会导致乱码或格式错误。

常见误区: 仅复制内容而未复制 '\0',导致后续内存垃圾值被当作字符输出。

完整可执行代码示例

以下代码展示了包含自定义字符串读取、长度计算以及核心复制逻辑的完整实现。注意 strmcpy'\0' 的传递。

#include <stdio.h>
#define MAXN 20

// 模拟裁判实现的读取函数
void ReadString( char s[] ) {
	gets(s);
}

// 核心部分复制函数
void strmcpy( char *t, int m, char *s ) {
    int i = 0;
    int len = 0;
    
    // 1. 计算源字符串长度
    while(t[len]) {
        len++;
    }
    
    // 2. 判断起始位置 m 是否有效 (假设 m 从 1 开始)
    if (m > len || m <= 0) {
        s[0] = '\0';
        return;
    }
    
    // 3. 从第 m 个字符开始复制,直到遇到 \0
    // 注意:t[m-1] 是第 m 个字符
    int src_idx = m - 1;
    while (t[src_idx] != '\0') {
        s[i] = t[src_idx];
        i++;
        src_idx++;
    }
    // 4. 手动添加字符串结束符,这是关键一步!
    s[i] = '\0';
}

int main()
{
    char t[MAXN], s[MAXN];
    int m;

    scanf("%d\n", &m); // 读取起始位置
    ReadString(t);     // 读取源字符串
    strmcpy(t, m, s);  // 执行复制
    printf("%s\n", s); // 输出结果

    return 0;
}

在该实现中,通过显式的 s[i] = '\0'; 确保了生成的子字符串是合法的 C 风格字符串。

第二部分:常用字符串与内存拷贝函数汇总

虽然手动实现有助于理解原理,但在实际工程中,应优先使用标准库函数。C 语言提供了多种拷贝方式,它们在使用场景和安全保障上存在显著差异。我们将逐一解析 strcpy、memcpy、strncpy 和 memmove。

1. strcpy:最基础的字符串复制

头文件: <string.h>

原型: char *strcpy(char *dest, const char *src);

工作原理:

将源字符串 src 的内容(包括最后的 '\0')逐个拷贝到目标地址 dest,直到遇到 src 的结束符为止。

  • 优点: 简单直接,自动处理结束符。
  • 风险: 不检查目标缓冲区大小。如果 src 长于 dest,将导致缓冲区溢出(Buffer Overflow),引发程序崩溃或安全漏洞。

适用场景: 已知源字符串长度小于目标缓冲区容量,且无需考虑其他类型数据。

char src[] = "Hello World";
char dest[20];
strcpy(dest, src); // 安全,因为 20 > 12

2. memcpy:通用内存块复制

头文件: <string.h> (C++) 为 <cstring>

原型: void *memcpy(void *dest, const void *src, size_t n);

工作原理:

从源地址 src 复制指定字节数 n 到目标地址 dest。

  • 数据类型无关性: 可以复制任何类型的数据(整型、结构体、浮点数等),因为它按字节操作。
  • 不识别结束符: 即使遇到 '\0' 也不会停止,严格按 n 拷贝。
  • 安全性警告: 同样不检查缓冲区重叠和大小限制。如果 dest 和 src 内存区域重叠,行为是未定义的(Undefined Behavior)。
struct Data { int a; float b; } src = {1, 2.0};
struct Data dest;
memcpy(&dest, &src, sizeof(struct Data));

3. strncpy:带长度限制的字符串复制

头文件: <string.h>

原型: char *strncpy(char *dest, const char *src, size_t n);

工作原理:

最多复制 src 的前 n 个字符到 dest。

关键陷阱(极易出错):

  1. 不会自动补零: 如果 src 的长度小于 n,strncpy 会将 dest 剩余部分填充为 '\0';但如果 src 长度大于或等于 n,dest **不会** 以 '\0' 结尾。
  2. 手动终止: 因此,使用 strncpy 后,通常建议手动设置 dest[n-1] = '\0'(前提是 dest 大小为 n)以确保字符串合法性。
char src[] = "TooLongString";
char dest[8];
strncpy(dest, src, 7); // 复制了7个字符
// dest 此时内容为 "TooLongS",没有 '\0'!
dest[7] = '\0'; // 必须手动补充

4. memmove:支持内存重叠的安全复制

头文件: <string.h>

原型: void *memmove(void *dest, const void *src, size_t n);

为什么需要它?

当源地址 src 和目标地址 dest 指向的内存区域发生重叠(Overlapping)时,memcpy 的行为是未定义的。而 memmove 能保证在所有情况下正确工作。

工作原理:

memmove 内部会检测重叠情况:

  • 若 dest < src(目标在源之前):可以从前往后复制,直接覆盖即可。
  • 若 dest > src(目标在源之后):可以从后往前复制,防止前面的数据被覆盖后影响后面的读取。

实例演示:向右移动数据

假设我们要将字符串末尾的一段内容向前移动,导致内存重叠:

#include <stdio.h>
#include <string.h>

int main () {
	char str[] = "memmove can be very useful......";
	/* 
	   将位于 15~25 的 "very useful" 
	   复制到 20~30 
	   源区间 [15, 25] 与 目标区间 [20, 30] 存在重叠 */
	memmove(str+20, str+15, 11);
	puts(str);
	return 0;
}

运行结果将变为:memmove can be very very useful...。如果使用 memcpy,由于重叠区域的覆盖顺序问题,结果可能会变成类似 memmove can be veeryyy useful... 的错误乱码。

总结与最佳实践建议

函数 目标 识别 \0 内存重叠处理 推荐指数
strcpy 字符串 是 否 低 (易溢出)
strncpy 字符串 否 (需手动) 否 中 (需小心尾部补零)
memcpy 任意数据 否 否 (未定义) 高 (无重叠时最快)
memmove 任意数据 否 是 (安全) 极高 (通用性最强)

在实际开发中:

  1. 字符串拷贝:优先使用 strncpy 并在最后手动确保 '\0',或者在现代 C 扩展中使用更安全的替代方案。
  2. 内存拷贝:只要涉及缓冲区可能重叠,一律使用 memmove;确定无重叠且追求极致性能时使用 memcpy。
  3. 永远不要信任未经检查的用户输入长度,在进行复制操作前务必验证源数据长度与目标缓冲区大小的关系。

以上就是C语言实现字符串部分复制及常用拷贝函数使用的详细内容,更多关于C语言字符串处理技巧的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表