导读:本期聚焦于小伙伴创作的《C语言中strcat和strncat的区别是什么?如何安全拼接字符串?》,敬请观看详情。缓冲区溢出是C语言字符串拼接最隐蔽的故障来源。标准库提供strcat与strncat两个函数,前者不检查目标空间剩余容量,直接把源串接到目的串末尾,遇结尾符停止;后者多接收一个长度参数,最多复制该数值的字符并自行补结束符。若目的地数组仅剩四个字节,用strcat接六字节源串会越界改写相邻内存,引发崩溃或逻辑错乱。strncat虽限制拷贝量,但需注意长度应按剩余空间减一计算,否则仍可能漏写终止符。理解二者对结束符的处理差异与边界判断方式,才能在嵌入式与系统编程中写出稳健的拼接逻辑。

在C语言里,字符串拼接是极常见的操作。标准库中的strcatstrncat都能把源字符串追加到目标字符串后面,但它们对内存边界和结束符的处理方式完全不同。理解这些差异,是避免缓冲区溢出漏洞的基础。

C语言中strcat和strncat的区别是什么?如何安全拼接字符串?

strcat的基本行为与隐患

strcat的函数原型为char *strcat(char *dest, const char *src)。它会先找到dest的当前结束符位置,然后从该位置开始逐个复制src中的字符,直到遇见src的结束符,最后在拼接结果后补一个''。这个过程完全不关心dest指向的数组到底还有多少空闲空间。

下面是一段典型用法,看似正常,实则埋雷:

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

int main() {
    char buf[8] = "ab";
    char *more = "cdefghij";
    strcat(buf, more);  /* buf只有6字节剩余,却要接9字节 */
    printf("%sn", buf);
    return 0;
}

上述代码中,buf总长为8,已用掉"ab"加结束符共3字节,剩余5字节。而more长度为9(含结束符),strcat会越界写入,破坏栈上其他数据。这类错误在测试时可能不崩溃,但在不同编译优化下就会暴露,且常被攻击者利用。

因此,strcat只适合在开发者能绝对保证目标缓冲区足够大的场景使用,例如长度在编译期就确定的常量拼接。任何涉及用户输入或可变长度的场景,都应避免直接使用它。

strncat的受限拼接机制

strncat的原型是char *strncat(char *dest, const char *src, size_t n)。相比strcat,它多了参数n,表示最多从src复制n个字符到dest。更重要的是,无论是否复制满n个字符,strncat都会保证dest以结束符结尾。

需要注意,n的语义是“最多复制的字符数”,而不是“dest的总容量”。实际编码时应传入“剩余空间减一”,为结束符留位置。示例:

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

int main() {
    char buf[8] = "ab";
    char *more = "cdefghij";
    size_t remain = sizeof(buf) - strlen(buf) - 1;  /* 剩余可写字符数 */
    strncat(buf, more, remain);
    printf("%sn", buf);  /* 输出 abcdefg */
    return 0;
}

这里remain计算为4,strncat只复制"cdef"四个字符,然后补结束符,buf内容变为"abcdefg",长度正好8,安全无越界。如果误把sizeof(buf)直接当n传入,则可能漏算结束符,虽不至于越界,但字符串可能未正常终止。

另外,若src长度小于n,strncat复制完src的结束符即停止,不会凑满n个字符。这个特性让它既安全又高效,是替代strcat的首选。

二者核心差异对比

从接口约束、结束符处理、安全性三方面可清晰区分两者:

对比维度strcatstrncat
长度参数无,依赖src结束符有,限制复制字符数
边界检查不检查目标剩余空间按n限制,需调用者算对
结束符保证总会补一个总会补一个
典型风险缓冲区溢出n算错导致截断或漏终止

可以看到,strncat把“安全阀门”交给了程序员:你必须正确计算剩余空间。如果算多了,仍会溢出;算少了,只是截断,不会崩。相对strcat的“必然危险”,strncat是“可控危险”。

在代码审查中,很多团队直接禁用strcat,规定所有拼接必须用strncat或更为现代的snprintf。例如用snprintf(buf, sizeof(buf), "%s%s", buf, more)也能达到受限拼接效果,且不易算错长度。

实践中的选用建议

当你面对一个已知固定长度且来源可信的拼接,比如拼接两个编译期常量路径片段,strcat简洁明了。但一旦目标缓冲区来自栈上数组、且源串来自外部,请立刻改用strncat,并养成用sizeof减strlen减一的习惯。

下面给出一个封装函数,降低手动计算出错概率:

#include <string.h>

/* 安全拼接,返回0表示完整拼接,非0表示被截断 */
int safe_cat(char *dest, size_t dest_size, const char *src) {
    size_t dlen = strlen(dest);
    if (dlen >= dest_size) return -1;  /* 已无空间 */
    size_t remain = dest_size - dlen - 1;
    strncat(dest, src, remain);
    return strlen(src) > remain ? 1 : 0;
}

该函数先确认dest当前长度未超限,再算剩余空间并调用strncat,最后通过源串长度与剩余空间比较判断是否发生截断。如此封装后,业务代码不再反复写长度计算逻辑,也统一了拼接失败的处理路径。

总结来说,strcat与strncat的根本区别在于“是否限制复制量”。在C语言没有原生边界检查的现实下,主动使用带长度参数的strncat,是写出健壮系统级代码的必备素养。

strcatstrncatstring_concatenation修改时间:2026-08-09 10:36:14

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。