C语言字符串函数使用避坑指南:从strcat内存错误到自定义实现

顾星河
顾星河
发布时间:2026-10-14| 6人浏览过
原创

许多C语言初学者在调用标准库函数如strcat时,容易混淆“字符数组”与“字符串常量指针”的内存属性差异,进而引发运行时崩溃。理解标准库的输入输出约束,并掌握基于动态内存分配的自定义拼接方案,是编写健壮C代码的关键。

问题现象:类型正确为何运行崩溃?

在处理字符串拼接时,strcat 是最常用的工具。其原型定义如下:

extern char *strcat(char *dest, char *src);

该函数功能是将 src 所指字符串追加到 dest 结尾(覆盖 dest 末尾的 '\0'),并重新添加结束符。src 和 dest 所指内存区域不可重叠,且 dest 必须有足够空间容纳结果。通常返回指向 dest 的指针。

场景一:正常使用

当 dest 定义为大小充足的字符数组时,程序运行正常:

复制代码 代码如下:
#include <stdio.h>
#include <string.h>

int main() {
    char d[30] = "Golden Global"; // 开辟可修改的内存空间
    const char *s = " View";       // 源字符串指针
    strcat(d, s);
    printf("%s", d); // 输出: Golden Global View
    return 0;
}

场景二:隐蔽的错误

如果将 dest 也声明为指向字符串常量的指针,即使类型匹配,程序也会执行失败:

复制代码 代码如下:
#include <stdio.h>
#include <string.h>

int main() {
    const char *p = "Golden Global"; // 指向只读内存区
    const char *s = " View";
    // 尝试修改只读内存,导致 Segmentation Fault
    strcat(p, s); 
    printf("%s", p);
    return 0;
}

虽然两个参数都是指针,但 p 指向的是编译期确定的常量字符串区(通常在数据段或代码段),通常是只读的。而 strcat 内部需要写入目标地址,从而触发非法访问。

深度解析:strcat 的底层实现逻辑

通过查看标准库中 strcat 的典型实现源码,可以清晰看出其内存操作行为:

复制代码 代码如下:
char *strcat(char *strDest, const char *strScr) {
    char *address = strDest; 
    assert((strDest != NULL) && (strScr != NULL));
    
    // 1. 移动 dest 指针至字符串末尾 '\0'
    while(*strDest) {
        strDest++; 
    }
    
    // 2. 循环拷贝 src 内容至 dest,直到遇到 '\0'
    while(*strDest++ = *strScr++) {
        ; // 空循环体
    }
    
    return address;
}

关键步骤 *strDest++ = *strScr++ 表明函数需要对 strDest 指向的地址进行赋值写操作。若 strDest 指向常量区或未被分配的野指针,编译器虽可能不报错,但运行时会因权限不足或内存无效而崩溃。数组名作为参数传递时,底层仍是地址,但该地址对应的是栈上预先分配好的连续可变内存,因此合法。

解决方案:自定义安全的字符串拼接函数

若希望像 C# 或其他高级语言那样,直接传入两个字符串指针即可得到拼接后的新字符串,需要手动管理内存。以下是自实现的 strcatDemo,利用 malloc 动态分配足够空间:

复制代码 代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

// 自定义拼接函数
char *strcatDemo(const char *str1, const char *str2) {
    // 1. 参数校验
    assert(str1 != NULL && str2 != NULL);
    
    // 2. 计算总长度并分配内存 (需额外1字节存储 \0)
    size_t len1 = strlen(str1);
    size_t len2 = strlen(str2);
    char *result = (char *)malloc(len1 + len2 + 1);
    
    if (result == NULL) {
        return NULL; // 内存分配失败处理
    }
    
    char *ptr = result;
    
    // 3. 拷贝第一部分
    while(*str1) {
        *ptr++ = *str1++;
    }
    
    // 4. 拷贝第二部分
    while(*str2) {
        *ptr++ = *str2++;
    }
    
    // 5. 添加结束符
    *ptr = '\0';
    
    return result;
}

int main() {
    const char *p = "hello, ";
    const char *s = "world!";
    
    char *t = strcatDemo(p, s);
    if (t) {
        puts(t); // 输出: hello, world!
        free(t); // 重要:释放动态内存
    }
    return 0;
}

此实现模拟了类似 C# 中字符串相加的特性,返回的新字符串独立于原字符串,使用者负责后续 free 释放内存,避免了目标缓冲区溢出或只读冲突的风险。

附录:常见字符串函数原型参考

为避免未来开发中出现类似问题,建议熟悉以下三个核心函数的逻辑:

1. strcpy:字符串复制

注意必须在复制完成后显式添加结束符,以防源串短于目的串残留旧数据。

char *strcpy(char *strDest, const char *strScr) {
    char *address = strDest;
    assert((strDest != NULL) && (strScr != NULL));
    while(*strScr) {
        *strDest++ = *strScr++;
    }
    *strDest = '\0'; // 关键步骤
    return address;
}

2. strcmp:字符串比较

逐个字符比较 ASCII 码,返回差值。

int strcmp(const char *str1, const char *str2) {
    assert(str1 != NULL && str2 != NULL);
    while(*str1 && *str2 && (*str1 == *str2)) {
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

3. strlen:长度计算

计算直到 '\0' 前的字符数。

size_t strlen(const char *str) {
    assert(str != NULL);
    size_t len = 0;
    while(*str++) {
        len++;
    }
    return len;
}

以上就是关于C语言字符串函数 strcat 常见陷阱分析与自定义实现的详细内容,更多关于C语言基础与内存管理的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。