C语言学习栏目目录

原创不易,转载请注明出处 ✨ 本文是 C 语言字符串系列的核心入门篇,超详细拆解“如何定义字符串” 和 “指针操作字符串”,从内存底层原理到逐行代码解析,零基础也能彻底吃透!

 前言:为什么字符串是 C 语言的 “重点 + 难点”?
12.1.1 在程序中定义字符串(4 种方式 + 文字版内存解析)
        方式 1:字符数组逐个初始化(手动加'\0')
        方式 2:字符数组直接赋值字符串(自动加'\0')
        方式 3:省略数组大小的字符数组
        方式 4:字符指针指向字符串字面量
12.1.2 指针和字符串(底层原理 + 超详细实战)
        指针操作字符串的核心原理(新手必背)
        实战 1:用指针遍历字符串(统计长度)
        实战 2:字符数组 vs 字符指针(核心区别超详细对比)
        实战 3:指针实现字符串拷贝(手写 strcpy)
        指针操作字符串的避坑指南(新手必看)
超详细总结
结尾语

前言:为什么字符串是 C 语言的 “重点 + 难点”?

C 语言没有像 Python、Java 那样的string专属类型 ——字符串在 C 语言中本质是 “以'\0'(空字符,ASCII 码为 0)结尾的字符数组”

新手学习字符串最容易踩的坑:

  • 忘记加'\0'导致字符串打印乱码;
  • 混淆 “字符数组” 和 “字符指针”,修改字符串时程序崩溃;
  • 分不清sizeof和字符串实际长度的区别;
  • 指针操作字符串时出现野指针、越界访问。

本文会从 “底层原理→语法规则→代码实战→避坑拆解” 四个维度,把字符串的定义和指针操作讲透,每个知识点都配文字版内存解析 + 逐行代码解析,确保所有平台都能正常显示,新手可直接照抄练习!

12.1.1 在程序中定义字符串(4 种方式 + 文字版内存解析)

字符串的核心要求:连续的字符序列 + 末尾必须有'\0'终止符'\0'是字符串的 “结束标志”,没有它,程序无法判断字符串在哪里结束)。

下面是 4 种定义字符串的方式,每种都包含「语法 + 代码 + 文字版内存解析 + 注意事项」,彻底替代之前的图表,确保清晰易懂。

方式 1:字符数组逐个初始化(手动加'\0'

语法规则
char 数组名[数组大小] = {'字符1', '字符2', ..., '字符n', '\0'};

数组大小必须 ≥ 实际字符数 + 1(多出来的 1 个位置留给'\0')。

完整代码示例
#include <stdio.h>

int main(void)
{
    // 定义:"Hello"有5个字符,数组大小设为6(5+1),最后一个元素手动加'\0'
    char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
    
    // 打印整个字符串(%s会从首地址读到'\0'停止)
    printf("1. 完整字符串:str1 = %s\n", str1);
    
    // 逐字符打印(验证每个元素的值)
    printf("2. 逐字符解析:\n");
    for (int i = 0; i < 6; i++) {
        // 打印下标、字符、ASCII值('\0'的ASCII是0)
        printf("   str1[%d] = '%c' (ASCII码:%d)\n", i, str1[i], str1[i]);
    }
    
    return 0;
}
输出结果
1. 完整字符串:str1 = Hello
2. 逐字符解析:
   str1[0] = 'H' (ASCII码:72)
   str1[1] = 'e' (ASCII码:101)
   str1[2] = 'l' (ASCII码:108)
   str1[3] = 'l' (ASCII码:108)
   str1[4] = 'o' (ASCII码:111)
   str1[5] = '' (ASCII码:0)
文字版内存解析(新手必看)

字符串str1存储在栈区(可读写区域),内存地址是连续的(示例地址仅用于理解,实际运行时会变化):

内存地址(示例) 存储内容(字符 / ASCII) 说明
0x7ffeefbff5a0 'H'(72) 数组第 0 个元素(首字符)
0x7ffeefbff5a1 'e'(101) 数组第 1 个元素
0x7ffeefbff5a2 'l'(108) 数组第 2 个元素
0x7ffeefbff5a3 'l'(108) 数组第 3 个元素
0x7ffeefbff5a4 'o'(111) 数组第 4 个元素
0x7ffeefbff5a5 '\0'(0) 字符串结束标志

关键:内存地址从低到高依次存储数组元素,每个字符占 1 字节,'\0'是必须的结束标志。

新手注意事项
  • 如果忘记加'\0',比如char str1[5] = {'H','e','l','l','o'},用%s打印时会出现乱码(程序会继续读取内存中str1[5]之后的随机数据,直到遇到'\0');
  • 数组大小不能小于字符数 + 1,比如char str1[5] = {'H','e','l','l','o','\0'}会触发编译报错(数组越界)。

方式 2:字符数组直接赋值字符串(自动加'\0'

这是实际开发中最常用的方式,编译器会自动在字符串末尾添加'\0',无需手动处理。

语法规则
char 数组名[数组大小] = "字符串内容";
完整代码示例
#include <stdio.h>

int main(void)
{
    // 定义:数组大小10,赋值"World"(5个字符),编译器自动加'\0'
    char str2[10] = "World";
    
    // 打印字符串
    printf("1. 完整字符串:str2 = %s\n", str2);
    
    // 关键对比:数组总大小 vs 字符串实际长度
    // sizeof(str2):计算数组占用的总字节数(数组大小×单个字符字节数)
    printf("2. 数组总字节数:sizeof(str2) = %zu\n", sizeof(str2));
    // 手动统计字符串实际长度(从首字符到'\0'的字符数,不含'\0')
    int actual_len = 0;
    while (str2[actual_len] != '\0') {
        actual_len++;
    }
    printf("3. 字符串实际长度(不含\\0):%d\n", actual_len);
    
    // 验证未使用的数组位置的值(默认是'\0')
    printf("4. 未使用位置的默认值:\n");
    for (int i = 5; i < 10; i++) {
        printf("   str2[%d] = '%c' (ASCII码:%d)\n", i, str2[i], str2[i]);
    }
    
    return 0;
}
输出结果
1. 完整字符串:str2 = World
2. 数组总字节数:sizeof(str2) = 10
3. 字符串实际长度(不含\0):5
4. 未使用位置的默认值:
   str2[5] = '' (ASCII码:0)
   str2[6] = '' (ASCII码:0)
   str2[7] = '' (ASCII码:0)
   str2[8] = '' (ASCII码:0)
   str2[9] = '' (ASCII码:0)
文字版内存解析

str2同样存储在栈区,数组大小 10,前 6 个位置存储字符串及'\0',剩余位置自动填充'\0'

内存地址(示例) 存储内容(字符 / ASCII) 说明
0x7ffeefbff5b0 'W'(87) 数组第 0 个元素
0x7ffeefbff5b1 'o'(111) 数组第 1 个元素
0x7ffeefbff5b2 'r'(114) 数组第 2 个元素
0x7ffeefbff5b3 'l'(108) 数组第 3 个元素
0x7ffeefbff5b4 'd'(100) 数组第 4 个元素
0x7ffeefbff5b5 '\0'(0) 编译器自动添加的结束标志
0x7ffeefbff5b6 '\0'(0) 未使用位置,默认填充
0x7ffeefbff5b7 '\0'(0) 未使用位置,默认填充
0x7ffeefbff5b8 '\0'(0) 未使用位置,默认填充
0x7ffeefbff5b9 '\0'(0) 未使用位置,默认填充
新手注意事项
  • 数组大小建议 “留有余量”:比如存储 “手机号 13800138000”(11 位),数组大小至少设为 12(11+1),避免后续拼接字符串时越界;
  • sizeof(数组名)计算的是数组总字节数,不是字符串实际长度(实际长度需要手动统计或用strlen函数)。

方式 3:省略数组大小的字符数组

如果不确定字符串长度,可省略数组大小,编译器会根据字符串内容自动计算数组长度(包含'\0')。

语法规则
char 数组名[] = "字符串内容";
完整代码示例
#include <stdio.h>

int main(void)
{
    // 定义:省略数组大小,编译器自动计算为6("Hello"5个字符 + '\0')
    char str3[] = "Hello";
    
    printf("1. 字符串:str3 = %s\n", str3);
    // 验证数组总长度(自动计算为6)
    printf("2. 数组总字节数:sizeof(str3) = %zu\n", sizeof(str3));
    // 验证每个元素
    printf("3. 所有元素值:\n");
    for (int i = 0; i < sizeof(str3)/sizeof(char); i++) {
        printf("   str3[%d] = '%c' (ASCII:%d)\n", i, str3[i], str3[i]);
    }
    
    return 0;
}
输出结果
1. 字符串:str3 = Hello
2. 数组总字节数:sizeof(str3) = 6
3. 所有元素值:
   str3[0] = 'H' (ASCII:72)
   str3[1] = 'e' (ASCII:101)
   str3[2] = 'l' (ASCII:108)
   str3[3] = 'l' (ASCII:108)
   str3[4] = 'o' (ASCII:111)
   str3[5] = '' (ASCII:0)
文字版内存解析

编译器自动计算数组长度为 6,存储在栈区,地址连续且刚好容纳字符串 +'\0'

内存地址(示例) 存储内容(字符 / ASCII) 说明
0x7ffeefbff5c0 'H'(72) 数组第 0 个元素
0x7ffeefbff5c1 'e'(101) 数组第 1 个元素
0x7ffeefbff5c2 'l'(108) 数组第 2 个元素
0x7ffeefbff5c3 'l'(108) 数组第 3 个元素
0x7ffeefbff5c4 'o'(111) 数组第 4 个元素
0x7ffeefbff5c5 '\0'(0) 自动添加的结束标志
新手注意事项
  • 优势:修改字符串内容时,无需同步修改数组大小(比如把"Hello"改成"Hello World",编译器会自动重新计算数组长度);
  • 缺点:数组长度固定,初始化后无法扩展(比如想在str3后拼接" C",会导致数组越界)。

方式 4:字符指针指向字符串字面量

char*(字符指针)指向字符串字面量(双引号""定义的字符串),是指针操作字符串的基础。

语法规则
char *指针名 = "字符串内容";
完整代码示例
#include <stdio.h>

int main(void)
{
    // 定义:字符指针str4指向字符串字面量"Hello C"的首地址
    char *str4 = "Hello C";
    
    printf("1. 字符串:str4 = %s\n", str4);
    // 方式1:下标访问(和数组一样)
    printf("2. 下标访问 str4[0] = %c\n", str4[0]); // 输出'H'
    printf("3. 下标访问 str4[6] = %c\n", str4[6]); // 输出'C'
    // 方式2:指针偏移访问(核心!)
    printf("4. 指针偏移 *(str4+1) = %c\n", *(str4+1)); // 输出'e'
    printf("5. 指针偏移 *(str4+5) = %c\n", *(str4+5)); // 输出空格
    // 打印指针本身的值(字符串首地址)
    printf("6. str4指针存储的地址:%p\n", str4);
    printf("7. str4+1的地址:%p\n", str4+1); // 地址+1(char占1字节)
    
    return 0;
}
输出结果
1. 字符串:str4 = Hello C
2. 下标访问 str4[0] = H
3. 下标访问 str4[6] = C
4. 指针偏移 *(str4+1) = e
5. 指针偏移 *(str4+5) =  
6. str4指针存储的地址:0x100003f00
7. str4+1的地址:0x100003f01
文字版内存解析(关键!)

这种方式的内存分布分为两部分,指针在栈区,字符串字面量在只读区

区域 内存地址(示例) 存储内容 说明
栈区(可写) 0x7ffeefbff5d0 0x100003f00(字符串首地址) str4 指针本身存储的内容
只读区(不可写) 0x100003f00 'H'(72) 字符串字面量的首字符
只读区(不可写) 0x100003f01 'e'(101) 字符串第 1 个字符
只读区(不可写) 0x100003f02 'l'(108) 字符串第 2 个字符
只读区(不可写) 0x100003f03 'l'(108) 字符串第 3 个字符
只读区(不可写) 0x100003f04 'o'(111) 字符串第 4 个字符
只读区(不可写) 0x100003f05 ' '(32) 字符串第 5 个字符(空格)
只读区(不可写) 0x100003f06 'C'(67) 字符串第 6 个字符
只读区(不可写) 0x100003f07 '\0'(0) 自动添加的结束标志

[!WARNING]重点:字符串字面量存储在内存的只读数据区(.rodata 段),只能读取不能修改,通过指针修改会导致程序崩溃!比如str4[0] = 'h'看似简单,实际运行时会触发 “段错误(Segmentation fault)”。

12.1.2 指针和字符串(底层原理 + 超详细实战)

字符串的本质是 “连续的字符内存块”,指针的本质是 “存储内存地址的变量”—— 用指针操作字符串,就是通过地址直接访问 / 修改字符,效率比数组下标更高(少了下标计算步骤)。

指针操作字符串的核心原理(新手必背)

表达式 含义
char *p = str p 指向字符串 str 的首字符地址(str 可以是字符数组 / 字符串字面量)
p[i] 等价于*(p+i):通过指针偏移访问第 i 个字符(下标从 0 开始)
*p 取 p 指向的地址中的字符(即字符串第一个字符)
p++ 指针后移 1 字节(char 占 1 字节),指向下一个字符
*p == '\0' 判断是否到字符串末尾(终止条件)

实战 1:用指针遍历字符串(统计长度)

需求

不使用strlen函数,用指针手动统计字符串长度(不含'\0')。

分步解析
  1. 定义字符指针指向字符串首地址;
  2. 初始化长度变量len=0
  3. 循环判断*p != '\0':满足则长度 + 1,指针后移;
  4. 循环结束后,len就是字符串实际长度。
完整代码
#include <stdio.h>

int main(void)
{
    char *str = "C Language"; // 目标字符串
    int len = 0;              // 存储长度
    char *p = str;            // 指针p指向字符串首地址
    
    printf("遍历过程:\n");
    // 循环遍历:直到*p为'\0'停止
    while (*p != '\0') {
        printf("   p当前地址:%p → 字符:'%c' → 长度暂存:%d\n", p, *p, len+1);
        len++;  // 长度+1
        p++;    // 指针后移1字节
    }
    
    // 最终结果
    printf("\n字符串:%s\n", str);
    printf("字符串实际长度(不含\\0):%d\n", len);
    
    return 0;
}
输出结果
遍历过程:
   p当前地址:0x100003f10 → 字符:'C' → 长度暂存:1
   p当前地址:0x100003f11 → 字符:' ' → 长度暂存:2
   p当前地址:0x100003f12 → 字符:'L' → 长度暂存:3
   p当前地址:0x100003f13 → 字符:'a' → 长度暂存:4
   p当前地址:0x100003f14 → 字符:'n' → 长度暂存:5
   p当前地址:0x100003f15 → 字符:'g' → 长度暂存:6
   p当前地址:0x100003f16 → 字符:'u' → 长度暂存:7
   p当前地址:0x100003f17 → 字符:'a' → 长度暂存:8
   p当前地址:0x100003f18 → 字符:'g' → 长度暂存:9
   p当前地址:0x100003f19 → 字符:'e' → 长度暂存:10

字符串:C Language
字符串实际长度(不含\0):10

实战 2:字符数组 vs 字符指针(核心区别超详细对比)

新手最易混淆的两个概念,用表格 + 代码 + 文字版内存解析彻底讲透。

核心区别表
对比维度 字符数组(char str [] = "xxx") 字符指针(char *str = "xxx")
内存存储位置 栈区(可读写区域) 指针存在栈区,指向只读数据区的字符串字面量
内容可修改 ✅ 可以修改(比如 str [0] = 'h') ❌ 直接修改会崩溃(只读区不可写)
数组大小 固定(初始化时确定,可通过 sizeof 获取) 无大小(仅存储地址,sizeof (str) 是指针长度,通常 8 字节)
赋值方式 仅初始化时可整体赋值,后续只能逐字符赋值 可重新指向其他字符串(比如 str = "new str")
底层本质 一块连续的字符内存块 一个存储地址的变量
代码验证(逐行解析)
#include <stdio.h>

int main(void)
{
    // ========== 第一部分:字符数组(可修改) ==========
    char str_arr[] = "Hello"; // 存储在栈区,可读写
    printf("【字符数组】初始值:%s\n", str_arr);
    // 修改第一个字符为小写h
    str_arr[0] = 'h'; 
    printf("【字符数组】修改后:%s\n", str_arr); // 输出hello
    // 打印数组大小(6字节:5字符+'\0')
    printf("【字符数组】sizeof(str_arr) = %zu\n\n", sizeof(str_arr));
    
    // ========== 第二部分:字符指针(不可直接修改) ==========
    char *str_ptr = "World"; // 指针指向只读区的字符串字面量
    printf("【字符指针】初始值:%s\n", str_ptr);
    // str_ptr[0] = 'w'; // ❌ 注释打开会崩溃!只读区不可写
    // 打印指针大小(8字节,64位系统指针长度固定为8)
    printf("【字符指针】sizeof(str_ptr) = %zu\n", sizeof(str_ptr));
    // 指针可重新指向其他字符串(合法!)
    str_ptr = "New World";
    printf("【字符指针】重新指向后:%s\n\n", str_ptr);
    
    // ========== 第三部分:指针指向字符数组(可修改) ==========
    char temp[] = "World";    // 先定义字符数组(栈区,可写)
    char *str_ptr2 = temp;    // 指针指向数组首地址
    printf("【指针指向数组】初始值:%s\n", str_ptr2);
    str_ptr2[0] = 'w';        // ✅ 合法:修改的是栈区的数组
    printf("【指针指向数组】修改后:%s\n", str_ptr2); // 输出world
    
    return 0;
}
输出结果
【字符数组】初始值:Hello
【字符数组】修改后:hello
【字符数组】sizeof(str_arr) = 6

【字符指针】初始值:World
【字符指针】sizeof(str_ptr) = 8
【字符指针】重新指向后:New World

【指针指向数组】初始值:World
【指针指向数组】修改后:world
文字版内存解析(关键!)
(1)字符数组(str_arr)的内存分布
区域 内存地址(示例) 存储内容(字符 / ASCII) 说明
栈区 0x7ffeefbff5e0 'h'(104) 修改后的第 0 个元素
栈区 0x7ffeefbff5e1 'e'(101) 第 1 个元素
栈区 0x7ffeefbff5e2 'l'(108) 第 2 个元素
栈区 0x7ffeefbff5e3 'l'(108) 第 3 个元素
栈区 0x7ffeefbff5e4 'o'(111) 第 4 个元素
栈区 0x7ffeefbff5e5 '\0'(0) 结束标志
(2)字符指针(str_ptr)的内存分布
区域 内存地址(示例) 存储内容 说明
栈区 0x7ffeefbff5f0 0x100003f20(初始地址) 初始指向 "World" 的首地址
栈区 0x7ffeefbff5f0 0x100003f30(修改后地址) 重新指向 "New World" 的首地址
只读区 0x100003f20 'W'(87) "World" 的首字符(不可修改)
只读区 0x100003f21 'o'(111) "World" 的第 1 个字符
... ... ... ...
只读区 0x100003f25 '\0'(0) "World" 的结束标志
只读区 0x100003f30 'N'(78) "New World" 的首字符(不可修改)
... ... ... ...
(3)指针指向数组(str_ptr2)的内存分布
区域 内存地址(示例) 存储内容(字符 / ASCII) 说明
栈区 0x7ffeefbff600 'w'(119) 修改后的第 0 个元素
栈区 0x7ffeefbff601 'o'(111) 第 1 个元素
栈区 0x7ffeefbff602 'r'(114) 第 2 个元素
栈区 0x7ffeefbff603 'l'(108) 第 3 个元素
栈区 0x7ffeefbff604 'd'(100) 第 4 个元素
栈区 0x7ffeefbff605 '\0'(0) 结束标志
栈区 0x7ffeefbff610 0x7ffeefbff600 str_ptr2 指针存储的数组首地址

实战 3:指针实现字符串拷贝(手写 strcpy)

需求

手动实现strcpy函数功能:将源字符串src拷贝到目标字符串dest,掌握指针操作字符串的核心逻辑。

核心思路
  1. 用两个指针分别指向destsrc的首地址;
  2. 循环将*src赋值给*dest,直到*src'\0'
  3. 最后给dest末尾手动加'\0'(确保目标字符串完整);
  4. const修饰src指针,防止误修改源字符串(编程好习惯)。
完整代码(逐行注释)
#include <stdio.h>

/**
 * 自定义字符串拷贝函数
 * @param dest 目标字符串(字符数组,需足够大)
 * @param src  源字符串(用const保护,防止修改)
 */
void my_strcpy(char *dest, const char *src)
{
    // 1. 定义临时指针,避免修改原指针(保留首地址)
    char *p_dest = dest;
    const char *p_src = src;
    
    // 2. 循环拷贝:直到*p_src为'\0'
    printf("拷贝过程:\n");
    while (*p_src != '\0') {
        *p_dest = *p_src; // 将源字符赋值给目标字符
        printf("   src地址:%p → '%c' → dest地址:%p\n", p_src, *p_src, p_dest);
        p_dest++; // 目标指针后移
        p_src++;  // 源指针后移
    }
    
    // 3. 目标字符串末尾加'\0'(关键!)
    *p_dest = '\0';
    printf("   最后给dest加\\0:dest地址:%p → 值:%d\n", p_dest, *p_dest);
}

int main(void)
{
    char dest[20]; // 目标数组(大小20,足够存储源字符串)
    char *src = "Hello C Language"; // 源字符串
    
    // 调用自定义拷贝函数
    my_strcpy(dest, src);
    
    // 验证结果
    printf("\n拷贝完成:\n");
    printf("   源字符串:%s\n", src);
    printf("   目标字符串:%s\n", dest);
    
    return 0;
}
输出结果
拷贝过程:
   src地址:0x100003f40 → 'H' → dest地址:0x7ffeefbff620
   src地址:0x100003f41 → 'e' → dest地址:0x7ffeefbff621
   src地址:0x100003f42 → 'l' → dest地址:0x7ffeefbff622
   src地址:0x100003f43 → 'l' → dest地址:0x7ffeefbff623
   src地址:0x100003f44 → 'o' → dest地址:0x7ffeefbff624
   src地址:0x100003f45 → ' ' → dest地址:0x7ffeefbff625
   src地址:0x100003f46 → 'C' → dest地址:0x7ffeefbff626
   src地址:0x100003f47 → ' ' → dest地址:0x7ffeefbff627
   src地址:0x100003f48 → 'L' → dest地址:0x7ffeefbff628
   src地址:0x100003f49 → 'a' → dest地址:0x7ffeefbff629
   src地址:0x100003f4a → 'n' → dest地址:0x7ffeefbff62a
   src地址:0x100003f4b → 'g' → dest地址:0x7ffeefbff62b
   src地址:0x100003f4c → 'u' → dest地址:0x7ffeefbff62c
   src地址:0x100003f4d → 'a' → dest地址:0x7ffeefbff62d
   src地址:0x100003f4e → 'g' → dest地址:0x7ffeefbff62e
   src地址:0x100003f4f → 'e' → dest地址:0x7ffeefbff62f
   最后给dest加\0:dest地址:0x7ffeefbff630 → 值:0

拷贝完成:
   源字符串:Hello C Language
   目标字符串:Hello C Language

指针操作字符串的避坑指南(新手必看)

坑 1:修改字符串字面量(最常见)
char *p = "test";
p[0] = 'T'; // ❌ 运行崩溃!字符串字面量在只读区,不可写

解决方案:先定义字符数组,再用指针指向数组:

char temp[] = "test";
char *p = temp;
p[0] = 'T'; // ✅ 合法
坑 2:野指针访问字符串
char *p; // ❌ 未初始化的野指针,指向随机地址
printf("%s\n", p); // 运行崩溃!

解决方案:指针必须先指向有效内存(字符数组 / 合法字符串):

char *p = "valid string"; // ✅ 指向合法字符串字面量
// 或
char temp[] = "valid string";
char *p = temp; // ✅ 指向字符数组
坑 3:忘记加'\0'导致乱码
char str[5] = {'a','b','c','d','e'}; // ❌ 无'\0'
printf("%s\n", str); // 输出abcde+乱码(越界读取)

解决方案:要么手动加'\0',要么直接赋值字符串(自动加'\0'):

char str[6] = {'a','b','c','d','e','\0'}; // ✅ 手动加
// 或
char str[] = "abcde"; // ✅ 自动加
坑 4:目标数组大小不足导致越界
char dest[5]; // ❌ 大小仅5,无法存储"Hello"(5字符+'\0'=6)
my_strcpy(dest, "Hello"); // 数组越界,程序可能崩溃/数据错乱

解决方案:目标数组大小必须≥源字符串长度 + 1:

char dest[6]; // ✅ 大小6,足够存储
my_strcpy(dest, "Hello");

超详细总结

  1. 字符串本质:以'\0'结尾的字符数组,'\0'是结束标志,缺失会导致乱码 / 越界;
  2. 字符串定义 4 种方式
    • 逐个初始化(手动加'\0'):适合需要精准控制每个字符的场景;
    • 直接赋值字符串(自动加'\0'):开发中最常用;
    • 省略数组大小:编译器自动计算长度,灵活但数组长度固定;
    • 字符指针指向字面量:指针可重定向,但字面量不可修改;
  3. 指针操作字符串核心
    • p[i]等价于*(p+i),遍历终止条件是*p == '\0'
    • 字符数组存储在栈区(可写),字符串字面量存储在只读区(不可写);
  4. 核心避坑点
    • 不修改字符串字面量,野指针必须先初始化;
    • 字符串拷贝时目标数组大小要足够,末尾必须加'\0'

掌握这些知识点,你已经能熟练处理 C 语言字符串的基础操作 —— 下一篇我们会讲解字符串输入,带你完成字符串的输入实战,敬请关注!

结尾语

如果本文对你有帮助,欢迎点赞 + 收藏 + 关注!学习过程中有任何疑问,评论区留言,我会第一时间解答~后续会持续更新 C 语言字符串进阶知识点(字符串 I/O、字符串函数、字符串拼接 / 分割),带你从入门到精通!

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐