首页 抖音推荐文章正文

c语言学习从内存看字符串操作:栈上搬家、拼接的那些事儿

抖音推荐 2025年08月28日 06:45 1 admin
c语言学习从内存看字符串操作:栈上搬家、拼接的那些事儿

#include <stdio.h>#include <string.h> int main (){   char str1[14] = "runoob";   char str2[14] = "google";   char str3[14];   int  len ;    /* 复制 str1 到 str3 */   strcpy(str3, str1);   printf("strcpy( str3, str1) :  %s\n", str3 );    /* 连接 str1 和 str2 */   strcat( str1, str2);   printf("strcat( str1, str2):   %s\n", str1 );    /* 连接后,str1 的总长度 */   len = strlen(str1);   printf("strlen(str1) :  %d\n", len );    return 0;}

从内存堆栈视角,给这段字符串操作代码做个 "内存 CT"

咱们先打个比方:字符数组就像超市里的一排储物格,每个格子放一个字符(像字母、标点),结尾必须留个 "终止符"('\0')当 "关门标记"。而栈就像超市的临时储物区,这些储物格用完就整体撤走,方便又高效。

先看懂代码干了啥

这段代码用字符数组定义了三个字符串,做了三件事:把str1复制到str3,把str2拼到str1后面,最后算str1的长度。运行结果会是:



plaintext

strcpy( str3, str1) :  runoobstrcat( str1, str2):   runoobgooglestrlen(str1) :  11



今天咱们不纠结结果,专注看这些字符串在栈上怎么放、怎么复制、怎么拼接。

内存区域聚焦:栈是唯一舞台

C 程序内存的三大块里,这段代码只用到了栈(所有字符数组和变量都在栈上):



  • 栈(Stack):像临时储物柜区,函数调用时自动分配连续空间,函数结束后自动释放。字符数组在栈上是连续的 "格子",每个格子存一个字符。
  • 全局区和堆:这段代码没用到,暂时忽略。

逐行拆解:字符串在栈上的 "搬家与拼接"

1.main函数启动:栈上划分 "字符储物区"

c

运行

int main () {   char str1[14] = "runoob";  // 14个字符的数组   char str2[14] = "google";  // 14个字符的数组   char str3[14];             // 14个字符的数组   int  len ;                 // 整数变量   // ...}



  • 程序运行时,main函数的栈帧在上创建,里面按顺序分配了四个变量的空间:str1[14]:14 个连续的char(每个 1 字节),共 14 字节。初始化 "runoob" 后,前 6 个格子放 'r','u','n','o','o','b',第 7 个放终止符 '\0',剩下的 7 个格子自动填 '\0'(默认初始化)。str2[14]:14 字节,初始化 "google" 后,前 6 个放 'g','o','o','g','l','e',第 7 个放 '\0',剩下的 7 个填 '\0'。str3[14]:14 字节,未初始化,初始值是栈上的随机垃圾值(像空柜子里的灰尘)。len:4 字节(int类型),用来存字符串长度。
  • 栈帧里的布局大概是这样(地址从高到低):
  • plaintext
高地址┌─────────────────────────────────┐│  len (4字节)                    │  ← 整数变量├─────────────────────────────────┤│  str3[13] ... str3[0] (14字节)  │  ← 字符数组3(未初始化)├─────────────────────────────────┤│  str2[13] ... str2[0] (14字节)  │  ← 字符数组2(存"google\0...")├─────────────────────────────────┤│  str1[13] ... str1[0] (14字节)  │  ← 字符数组1(存"runoob\0...")├─────────────────────────────────┤│  main函数其他信息(返回地址等)  │低地址

2.strcpy(str3, str1):复制字符串(给 str3"搬家")

c

运行

strcpy(str3, str1);  // 把str1的内容复制到str3



  • strcpy函数的工作原理:从str1的第一个字符开始,逐个复制到str3,直到遇到str1里的终止符 '\0'(连终止符一起复制)。
  • 内存操作:栈上str3的格子被依次填入str1的内容 —— 前 6 个格子变成 'r','u','n','o','o','b',第 7 个格子填 '\0',后面的格子不变(但此时已无关紧要,因为终止符标志字符串结束)。
  • 就像把str1的 "物品"(字符)逐个搬到str3的柜子里,直到看到 "关门标记"('\0')为止。

3.strcat(str1, str2):拼接字符串(给 str1"扩容")

c

运行

strcat(str1, str2);  // 把str2拼到str1后面



  • strcat函数的工作原理:先找到str1里的终止符 '\0',然后从这个位置开始,逐个复制str2的字符(包括str2的终止符 '\0')。
  • 内存操作:str1原来的终止符在第 6 个字符后(索引 6),现在从这里开始填入str2的内容 —— 索引 6 到 11 变成 'g','o','o','g','l','e',索引 12 填 '\0'。此时str1里存的是 "runoobgoogle\0"。
  • 关键:str1必须有足够空间(定义时是 14 字节),否则会溢出(后面的内存被覆盖)。这里刚好够("runoob"6 + "google"6 + 终止符 1 = 13 字节 < 14),侥幸没出事。

4.strlen(str1):计算长度(数字符,不算终止符)

c

运行

len = strlen(str1);  // 算str1的长度



  • strlen函数的工作原理:从str1的第一个字符开始数,直到遇到终止符 '\0',计数停止(不包括终止符)。
  • 内存操作:从str1的起始地址开始逐个检查字符,数到第 11 个字符('e')后遇到 '\0',所以返回 11,存入len变量(栈上的 4 字节空间)。

5. 程序结束:栈帧 "整体清空"

c

运行

return 0;



  • main函数执行完,整个栈帧被自动释放,str1、str2、str3和len的内存地址不再有效(就像临时储物区的所有柜子都被撤走)。

字符串操作的内存风险:"别把柜子撑破了"

  • 溢出危险:如果str1定义时不够大(比如char str1[10]),strcat拼接后会超出 10 字节,覆盖栈上其他变量的内存(像柜子太小吃不下,东西洒到别人柜子里),可能导致程序崩溃或安全漏洞。
  • 终止符重要性:如果字符串没有 '\0',strcpy、strcat、strlen会一直找下去,直到随机遇到一个 '\0',导致结果错乱(就像没关门的柜子,别人不知道哪里算结束)。

关键结论:栈上的字符串是 "带终止符的字符队列"

  • 字符数组在栈上连续存储,每个元素占 1 字节,必须以 '\0' 结尾(否则函数不认)。
  • 字符串操作函数(strcpy、strcat等)本质是在栈上搬运字符,依赖终止符判断边界。
  • 栈的自动管理让字符串使用方便,但必须保证数组大小足够,否则会 "撑破栈"(溢出)。

标题:

  1. 《C 语言字符串:栈上的字符队列,终止符是 "关门标记"》
  2. 《从内存看字符串操作:栈上搬家、拼接的那些事儿》

简介:

通过分析字符串操作代码的内存分配,揭示字符数组在栈上连续存储、以终止符 '\0' 为结束标志的特性,解析strcpy、strcat、strlen在栈上的工作原理,提醒字符串溢出的风险。

关键词:

#C 语言字符串 #栈内存 #字符数组 #字符串操作 #终止符

发表评论

泰日号Copyright Your WebSite.Some Rights Reserved. 网站地图 备案号:川ICP备66666666号 Z-BlogPHP强力驱动