接续上文:暑期自学嵌入式——Day05(C语言阶段)-CSDN博客
主页点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
主页:
一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm=1010.2135.3001.5343
Day05(补充)
字符数组和字符串:字符串逆序输出详解
一、字符串逆序输出的实现思路与方法
1. 两种核心实现思路
实现方式 | 核心逻辑 | 优缺点分析 |
---|---|---|
仅逆序显示(不修改原串) | 不改变字符串存储顺序,仅从最后一个有效字符倒序打印到第一个字符 | 优点:简单直接,不破坏原数据;缺点:未真正修改字符串(仅输出层面逆序) |
原地逆序(修改原串后显示) | 通过交换字符位置,将原字符串改为逆序(如 "abc"→"cba"),再正常打印 | 优点:真正实现字符串逆序存储;缺点:需修改原数据,逻辑稍复杂 |
2. 基础实现:仅逆序显示(不修改原串)
(1)核心步骤
获取输入:用
gets
或scanf
读取字符串(gets
支持空格,scanf
遇空格停止)。计算有效长度:用
strlen
获取字符串实际长度(不含'\0'
)。倒序循环输出:从
len-1
(最后一个字符下标)循环到0
,逐个输出字符。
(2)示例代码
#include <stdio.h> #include <string.h> #define N 20 // 数组大小 int main() { char arr[N]; printf("Please input a string: "); gets(arr); // 获取输入(支持空格,如"hello world") int len = strlen(arr); // 计算有效长度(不含'\0') printf("逆序输出:"); // 从最后一个字符(len-1)倒序输出到第一个(0) for (int i = len - 1; i >= 0; i--) { putchar(arr[i]); // 逐个输出字符 } putchar('\n'); // 手动换行 return 0; }
(3)关键技巧
strlen
vssizeof
:strlen(arr)
:返回字符串有效长度(仅'\0'
之前的字符数,如 "hello" 返回 5)。sizeof(arr)
:返回数组总空间(定义为N=20
,则返回 20,包含未使用的空间)。必须用
strlen
:避免打印未初始化的垃圾字符(如数组定义为 20,但输入仅 5 个字符)。
3. 进阶实现:原地逆序(修改原串)
(1)核心算法:双指针法
指针定义:
i
:从头部开始(初始i=0
)。j
:从尾部开始(初始j = strlen(arr) - 1
)。
操作逻辑:交换
arr[i]
和arr[j]
,然后i++
、j--
,直到i >= j
(两指针相遇,所有字符交换完成)。
(2)示例代码
#include <stdio.h> #include <string.h> #define N 20 int main() { char arr[N]; printf("Please input a string: "); gets(arr); int len = strlen(arr); // 双指针交换(原地逆序) int i = 0; // 头指针 int j = len - 1; // 尾指针 while (i < j) { // 两指针未相遇时循环 char temp = arr[i]; // 临时变量辅助交换 arr[i] = arr[j]; arr[j] = temp; i++; // 头指针后移 j--; // 尾指针前移 } // 输出逆序后的字符串 printf("逆序后的字符串:%s\n", arr); // 此时arr已变为逆序 return 0; }
(3)过程演示(以输入 "apple" 为例)
步骤 | i |
j |
交换前 | 交换后 | 操作说明 |
---|---|---|---|---|---|
1 | 0 | 4 | "apple" | "eppla" | 交换a[0] ('a')和a[4] ('e') |
2 | 1 | 3 | "eppla" | "eplpa" | 交换a[1] ('p')和a[3] ('l') |
3 | 2 | 2 | "eplpa" | (停止) | i >= j ,循环终止 |
最终结果 | - | - | - | "elppa" | 完成逆序 |
二、输入函数的选择与注意事项
1. gets
、scanf
、fgets
对比
函数 | 特点 | 安全隐患 / 注意事项 |
---|---|---|
gets(arr) |
读取整行字符串(支持空格),直到换行符为止(自动忽略换行符,添加'\0' ) |
无长度限制,若输入超过数组大小会导致越界(已弃用,编译器会警告) |
scanf("%s", arr) |
遇空格、换行等分隔符停止读取,自动添加'\0' |
无法读取带空格的字符串(如 "hello world" 只能读到 "hello") |
fgets(arr, N, stdin) |
读取最多N-1 个字符(预留'\0' ),保留换行符,更安全 |
需手动处理换行符(如arr[strcspn(arr, "\n")] = '\0' 去除换行);推荐使用 |
2. 输入函数使用建议
读取带空格的字符串:优先用
fgets
(安全),避免gets
(有溢出风险)。读取无空格的字符串:
scanf("%s", arr)
更简洁(注意限制输入长度,如scanf("%19s", arr)
,数组大小为 20 时)。示例(
fgets
替代gets
):#include <stdio.h> #include <string.h> int main() { char arr[20]; printf("请输入字符串(支持空格):"); fgets(arr, 20, stdin); // 最多读19个字符(留1个给'\0') // 去除fgets保留的换行符(若有) arr[strcspn(arr, "\n")] = '\0'; // strcspn计算到'\n'的长度 printf("你输入的是:%s\n", arr); return 0; }
三、知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
逆序显示(方法 1) | 倒序循环输出(i从len-1到0 ),不修改原串;依赖strlen 获取有效长度 |
sizeof (总空间)与strlen (有效长度)的区别;避免打印未初始化字符 |
⭐⭐⭐ |
原地逆序(方法 2) | 双指针法交换字符(i=0 与j=len-1 交换,直到i>=j );需临时变量辅助 |
循环终止条件(i < j 而非i <= j ,避免重复交换);奇 / 偶长度字符串的处理(自动兼容) |
⭐⭐⭐⭐ |
输入函数对比 | gets 支持空格但不安全;scanf 遇空格停止;fgets 安全且支持空格 |
gets 的溢出风险;fgets 保留换行符的处理方法 |
⭐⭐ |
字符串长度计算 | strlen 返回有效长度(不含'\0' ),需包含<string.h> ;也可手动循环计算 |
手动计算长度的循环逻辑(while(arr[i] != '\0') i++ ) |
⭐⭐ |
数组越界预防 | 输入时限制长度(如fgets 的N 参数、scanf 的%19s );初始化数组避免垃圾值 |
gets 无长度限制导致的溢出;未初始化数组的随机值打印 |
⭐⭐⭐ |
四、编程技巧
调试技巧: 逆序逻辑出错时,可打印中间过程(如双指针交换时的
i
、j
值和数组内容):while (i < j) { printf("交换前:i=%d, j=%d, arr=%s\n", i, j, arr); // 交换代码 printf("交换后:i=%d, j=%d, arr=%s\n", i, j, arr); }
边界条件处理: 考虑特殊输入(如空字符串
""
、单字符"a"
),确保程序不崩溃:空字符串:
strlen
返回 0,循环不执行(正确)。单字符:
i=0
,j=0
,i < j
不成立,循环不执行(无需交换,正确)。
函数封装: 将逆序逻辑封装为函数,提高复用性:
// 原地逆序函数 void reverseString(char* arr) { int len = strlen(arr); int i = 0, j = len - 1; while (i < j) { char temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i++; j--; } }
通过以上内容,可掌握字符串逆序的两种核心方法,理解输入函数的特性及安全用法,同时规避数组越界等常见问题。
字符串函数:从基础到应用
一、字符串函数概述
1. 核心概念
定义:C 语言标准库(
string.h
头文件)中封装的专用函数,用于简化字符串操作(如计算长度、拷贝、比较等)。重要性:字符串是编程中最常用的数据形式之一,直接手写逻辑(如遍历计算长度)低效且易出错,函数封装可大幅提升开发效率。
使用前提:所有字符串函数必须通过
#include <string.h>
声明头文件,否则编译器会报错(“未声明的函数”)。
二、常用字符串函数详解
1. 字符串长度函数 strlen
(1)功能与原型
功能:计算字符串中 有效字符的个数(从首字符开始,直到遇到
'\0'
为止,不含'\0'
)。函数原型:
size_t strlen(const char *s); // size_t是无符号整数类型(unsigned int)
参数
s
:字符串的首地址(字符数组名或字符串常量)。返回值:有效字符数(无符号整数,如
"abc"
返回 3)。
(2)应用示例与解析
基础示例
#include <stdio.h> #include <string.h> int main() { // 示例1:显式包含'\0'的字符数组 char s1[10] = {'A', '\0', 'B', 'C', '\0', 'D'}; printf("s1长度:%zu\n", strlen(s1)); // 结果:1(遇到第一个'\0'即停止) // 示例2:字符串常量初始化(隐含'\0') char s2[] = "maker"; // 存储为'm','a','k','e','r','\0' printf("s2长度:%zu\n", strlen(s2)); // 结果:5(不含'\0') return 0; }
转义字符处理
转义字符(如 \t
、\n
)虽由多个字符组成(反斜杠 + 字母),但在字符串中视为 单个字符,strlen
计为 1。
// 示例:转义字符的长度计算 char s3[] = "\tab\n\v\w\e"; // 包含\t、\a、\n、\v、\w、\e(共6个字符) printf("s3长度:%zu\n", strlen(s3)); // 结果:6
特殊编码(十六进制 / 八进制)
字符串中可通过 \xhh
(十六进制)或 \ooo
(八进制)表示字符,strlen
计为 1 个字符。
// 示例:十六进制和八进制编码 char s4[] = "\x69\141"; // \x69是十六进制69(ASCII码105,对应'i');\141是八进制141(ASCII码97,对应'a') printf("s4内容:%s\n", s4); // 输出"ia" printf("s4长度:%zu\n", strlen(s4)); // 结果:2
(3)strlen
与 sizeof
的核心区别
对比项 | strlen(s) |
sizeof(s) |
---|---|---|
本质 | 函数,计算 有效字符数(不含 '\0' ) |
运算符,计算 变量 / 类型的总内存字节数(含 '\0' 及未使用空间) |
示例(char s[] = "maker" ) |
返回 5('m','a','k','e','r' ) |
返回 6(5 个字符 + 1 个 '\0' ) |
示例(char s[10] = "maker" ) |
返回 5(有效字符不变) | 返回 10(数组总空间固定为 10 字节) |
计算时机 | 运行时计算(遍历字符串直到 '\0' ) |
编译时计算(已知类型 / 数组大小) |
2. 其他重要字符串函数(基础认知)
函数 | 功能 | 原型示例 | 关键注意事项 |
---|---|---|---|
strcpy |
将源字符串拷贝到目标数组(包括 '\0' ) |
char* strcpy(char *dest, const char *src); |
目标数组需足够大(否则越界);源字符串必须以 '\0' 结尾 |
strcat |
将源字符串连接到目标字符串末尾(覆盖目标原 '\0' ,添加新 '\0' ) |
char* strcat(char *dest, const char *src); |
目标数组需预留足够空间;源和目标不能重叠(如 strcat(s, s) 会出错) |
strcmp |
按 ASCII 码比较两个字符串(逐个字符对比,直到不同或 '\0' ) |
int strcmp(const char *s1, const char *s2); |
返回值:s1 > s2 为正,s1 < s2 为负,相等为 0;不能用 == 直接比较字符串 |
三、知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
字符串函数基础 | 需包含 <string.h> ;封装高频操作(如 strlen 计算长度) |
忘记包含头文件导致的编译错误;函数参数类型(const char* 表示只读) |
⭐⭐ |
strlen 函数 |
计算有效字符数(不含 '\0' );转义字符、特殊编码均计为 1 个字符 |
与 sizeof 的区别(运行时 vs 编译时;有效字符 vs 总内存);遇到第一个 '\0' 即停止 |
⭐⭐⭐ |
转义字符与特殊编码 | \t 、\n 等转义字符计为 1;\x69 (十六进制)、\141 (八进制)对应单个字符 |
八进制 / 十六进制编码的转换(如 \141 对应 'a' );转义字符的计数规则 |
⭐⭐⭐⭐ |
其他函数(strcpy 等) |
strcpy 拷贝、strcat 连接、strcmp 比较;均依赖 '\0' 作为终止标志 |
strcpy 的越界风险;strcmp 的返回值判断(非 0 即不等,正 / 负表示大小) |
⭐⭐⭐ |
四、编程建议
strlen
使用注意:仅用于以
'\0'
结尾的字符串(字符数组无'\0'
时,strlen
会越界读取,结果随机)。返回值是
size_t
(无符号),避免与有符号整数比较(如if (strlen(s) >= -1)
永远成立,因无符号不会为负)。
sizeof
与strlen
对比记忆:记口诀:
strlen
算 “内容长度”(有效字符),sizeof
算 “房子大小”(总内存)。示例:
char s[10] = "abc"
→strlen=3
(内容),sizeof=10
(房子)。
函数学习方法: 先掌握
strlen
等基础函数的用法和原理,遇到未知函数(如strstr
查找子串)时,学会查阅 C 语言手册(如cplusplus.com),重点关注功能、参数、返回值和注意事项。
通过以上内容,可掌握 strlen
的核心用法及字符串函数的基础认知,明确 strlen
与 sizeof
的关键区别,为后续学习字符串拷贝、比较等函数奠定基础。
字符串函数:字符串拷贝函数strcpy
详解
一、strcpy
函数的基本介绍
1. 函数格式与核心功能
函数原型:
char *strcpy(char *dest, const char *src);
核心功能:将源字符串(
src
)完整拷贝到目标字符数组(dest
)中,包括字符串结束符'\0'
,最终目标数组成为源字符串的副本。参数与返回值:
dest
:目标字符数组(必须是可修改的左值,如char arr[20]
,不能是字符串常量)。src
:源字符串(可以是字符数组或字符串常量,如"hello"
,必须以'\0'
结尾)。返回值:目标数组
dest
的首地址(方便链式调用,如printf("%s", strcpy(dest, src))
)。
2. 关键使用规则(必须遵守)
目标数组空间必须足够大: 需能容纳源字符串的所有字符(包括
'\0'
),否则会导致缓冲区溢出(覆盖相邻内存,程序可能崩溃或输出乱码)。示例:源字符串
"hello"
(长度 5,含'\0'
共 6 字节),目标数组至少需 6 字节(如char dest[6]
)。
源字符串必须以
'\0'
结尾:strcpy
通过'\0'
判断拷贝结束,若源字符串无'\0'
,会越界拷贝(直到意外遇到'\0'
)。源和目标不能重叠: 如
strcpy(arr+2, arr)
(目标是源的一部分)会导致拷贝结果异常(标准未定义行为)。数组名不能直接赋值: 错误写法:
dest = src;
(数组名是地址常量,不能被修改),必须用strcpy
逐字符拷贝。
二、strcpy
函数的使用示例与原理
1. 基础使用示例
#include <stdio.h> #include <string.h> int main() { char src[] = "hello world"; // 源字符串(含'\0'共12字节) char dest[20]; // 目标数组(空间足够大) // 拷贝src到dest strcpy(dest, src); // 输出结果(两者内容相同) printf("源字符串:%s(地址:%p)\n", src, src); printf("目标数组:%s(地址:%p)\n", dest, dest); return 0; }
输出结果: 源字符串和目标数组均输出
hello world
,证明拷贝完整(包括'\0'
)。
2. strcpy
的手动实现(理解底层原理)
strcpy
本质是通过循环逐字符拷贝,直到遇到源字符串的'\0'
并拷贝它。
// 手动实现strcpy功能(简化版) char* my_strcpy(char* dest, const char* src) { // 保存目标首地址(用于返回) char* original_dest = dest; // 逐字符拷贝(包括'\0') while (*src != '\0') { *dest = *src; // 拷贝当前字符 dest++; // 目标指针后移 src++; // 源指针后移 } *dest = '\0'; // 拷贝结束符(关键!) return original_dest; // 返回目标首地址 }
核心逻辑: 循环拷贝每个字符(
*src
)到目标地址(*dest
),直到*src
为'\0'
,最后单独拷贝'\0'
(确保目标数组以'\0'
结尾)。
3. 常见错误与规避
(1)目标数组空间不足(高危)
char src[] = "this is a long string"; // 长度21(含'\0'共22字节) char dest[10]; // 空间不足(仅10字节) strcpy(dest, src); // 缓冲区溢出!覆盖相邻内存,程序可能崩溃
规避方法:
拷贝前检查目标数组长度是否 ≥ 源字符串长度 + 1(
strlen(src)+1
):
if (sizeof(dest) >= strlen(src) + 1) { strcpy(dest, src); // 安全拷贝 } else { printf("目标空间不足!\n"); }
(2)忘记拷贝'\0'
(手动实现时)
// 错误示例:循环条件遗漏'\0' void bad_copy(char* dest, const char* src) { int i = 0; while (src[i] != '\0') { // 仅拷贝到'\0'前的字符 dest[i] = src[i]; i++; } // 未拷贝'\0',dest不是合法字符串
后果:目标数组无
'\0'
,用%s
输出时会越界读取(乱码)。正确做法:循环结束后手动添加'\0':
dest[i] = '\0'; // 必须添加结束符
(3)直接赋值数组名(语法错误)
char dest[20], src[] = "hello"; dest = src; // 错误!数组名是地址常量,不能被赋值
原因:数组名代表首地址(常量),不能作为左值修改,必须通过
strcpy
逐字符拷贝。
二、strcpy
函数的总结与对比
核心要点 | 说明 | 易错点提醒 |
---|---|---|
拷贝范围 | 从src[0] 到src[n] (n 为strlen(src) ),包括src[n] (即'\0' ) |
手动实现时需确保'\0' 被拷贝(否则目标数组不是合法字符串) |
目标数组要求 | 必须可修改(非字符串常量)、空间足够(≥strlen(src)+1 ) |
字符串常量(如"test" )不能作为dest (const 类型不可修改) |
与直接赋值的区别 | strcpy 是逐字符拷贝内容,dest = src 是地址赋值(语法错误) |
数组名是常量,不能直接赋值,必须用strcpy |
安全性 | 本身不检查空间(需程序员手动确保),存在溢出风险 | 实际开发中可使用strncpy (指定最大拷贝长度)替代,更安全 |
三、知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
strcpy 基本用法 |
格式:strcpy(dest, src) ;拷贝源字符串(含'\0' )到目标数组;返回dest 首地址 |
参数顺序(dest 在前,src 在后);必须包含'\0' 的拷贝 |
⭐⭐⭐ |
空间要求 | 目标数组长度 ≥ strlen(src) + 1 (否则溢出) |
缓冲区溢出的后果(程序崩溃、乱码);如何提前检查空间(sizeof(dest) >= ... ) |
⭐⭐⭐⭐ |
手动实现原理 | 循环逐字符拷贝,最后拷贝'\0' ;返回目标首地址 |
循环终止条件(需包含'\0' );手动添加'\0' 的必要性 |
⭐⭐⭐ |
常见错误 | 目标空间不足、忘记拷贝'\0' 、直接赋值数组名 |
数组名不能直接赋值(dest = src 错误);'\0' 对字符串的重要性(%s 输出依赖) |
⭐⭐⭐ |
安全性提升 | 可使用strncpy(dest, src, n) 限制拷贝长度(n 为目标数组最大容量 - 1) |
strncpy 需手动添加'\0' (若源字符串超长);与strcpy 的适用场景区别 |
⭐⭐⭐ |
四、编程建议
优先使用标准库函数: 理解
strcpy
原理后,实际开发中直接使用标准库版本(经过优化和测试,更可靠),无需重复造轮子。强制检查空间: 拷贝前必须验证目标数组长度 ≥ 源字符串长度 + 1,例如:
#define DEST_LEN 20 char dest[DEST_LEN]; char src[] = "example"; if (strlen(src) + 1 <= DEST_LEN) { strcpy(dest, src); // 安全 } else { // 处理空间不足(如截断或提示错误) }
了解替代函数:
strncpy(dest, src, n)
可指定最大拷贝长度(n
),避免溢出(但需手动添加'\0'
):strncpy(dest, src, DEST_LEN - 1); // 最多拷贝19个字符(留1个给'\0') dest[DEST_LEN - 1] = '\0'; // 确保结尾有'\0'
通过以上内容,可掌握strcpy
的用法、原理及安全注意事项,明确其在字符串操作中的核心作用 —— 实现字符串的完整拷贝,同时规避缓冲区溢出等高危错误。
字符串函数(进阶):strcat 与 strcmp 详解
一、字符串连接函数strcat
1. 函数基础与核心功能
函数原型:
char *strcat(char *dest, const char *src);
功能:将源字符串(
src
)连接到目标字符数组(dest
)的末尾,形成新的字符串。核心逻辑:
在
dest
中寻找第一个'\0'
(原字符串的结尾);从该位置开始,将
src
的字符(包括src
的'\0'
)逐个拷贝到dest
;最终
dest
以src
的'\0'
结尾,原dest
的'\0'
被覆盖。
示例:
#include <stdio.h> #include <string.h> int main() { char dest[20] = "Hello"; // 目标数组(初始字符串:"Hello\0...") char src[] = " World"; // 源字符串(" World\0") strcat(dest, src); // 连接:在dest的'\0'位置开始拷贝src printf("连接后:%s\n", dest); // 输出"Hello World" return 0; }
2. 使用规则与注意事项
(1)必须满足的前提条件
dest
和src
均为合法字符串(均以'\0'
结尾):若
dest
无'\0'
,strcat
会越界寻找结尾(结果随机);若
src
无'\0'
,会越界拷贝(覆盖dest
后续内存)。
dest
空间必须足够大: 所需空间 =strlen(dest) + strlen(src) + 1
(原dest
长度 +src
长度 + 新'\0'
)。示例:
dest
初始长度 5("Hello"),src
长度 6("World"),需至少 5+6+1=12 字节(dest
定义为[20]
足够)。
dest
必须是可修改的字符数组: 不能是字符串常量(如strcat("Hello", "World")
错误,字符串常量不可修改)。
3. 常见错误与规避
(1)空间不足导致溢出
char dest[10] = "Hello"; // 空间10字节(当前已用6字节:5字符+'\0') char src[] = " World"; // 需6字节(5字符+'\0') strcat(dest, src); // 总需6+6=12字节 > 10 → 溢出!
后果:覆盖
dest
相邻内存,可能导致程序崩溃或输出乱码。规避:连接前检查空间:
if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) { strcat(dest, src); // 安全连接 } else { printf("目标空间不足!\n"); }
(2)dest
不是合法字符串(无'\0'
)
char dest[10] = {'H', 'i'}; // 未显式添加'\0',且未初始化的元素可能非0 char src[] = "!"; strcat(dest, src); // 寻找'\0'时越界,可能将src连接到随机位置
后果:连接位置错误(可能覆盖
dest
的有效字符)。规避:确保
dest
初始化为合法字符串:
char dest[10] = "Hi"; // 双引号初始化,自动添加'\0'(安全)
4. 综合应用:组合strcpy
与strcat
通过先拷贝、再连接,可构建复杂字符串:
// 示例:构建"Turbo C++" char result[20]; // 步骤1:先拷贝"Turbo"到result strcpy(result, "Turbo"); // 步骤2:连接空格 strcat(result, " "); // 步骤3:连接"C++" strcat(result, "C++"); printf("结果:%s\n", result); // 输出"Turbo C++"
优势:每次操作自动维护
'\0'
位置,无需手动管理结尾。
二、字符串比较函数strcmp
1. 函数基础与比较规则
函数原型:
int strcmp(const char *str1, const char *str2);
功能:按 ASCII 码值逐字符比较两个字符串,返回比较结果。
比较规则:
从第一个字符开始,逐个比较对应位置的字符(
str1[i]
vsstr2[i]
);若遇到不同字符,返回两者 ASCII 码的差值(
str1[i] - str2[i]
);若所有字符相同,比较长度:
长度相同(均到
'\0'
):返回 0(相等);长度不同(一个先到
'\0'
):返回 “较长字符串 - 较短字符串” 的差值(通常简化为 ±1)。
返回值约定:
>0
:str1 > str2
(str1
在字典序中更靠后);=0
:str1 == str2
(完全相同);<0
:str1 < str2
(str1
在字典序中更靠前)。
2. 比较示例与解析
示例 | 比较过程 | 返回值 | 结论 |
---|---|---|---|
strcmp("ABC", "ABC") |
所有字符相同,均到'\0' |
0 | 相等 |
strcmp("BBC", "ABC") |
第一个字符:'B'(66) > 'A'(65),停止比较 | 1 | str1 > str2 |
strcmp("ABD", "ABC") |
前两位相同,第三位 'D'(68) > 'C'(67),停止比较 | 1 | str1 > str2 |
strcmp("AB", "ABC") |
前两位相同,str1 先到'\0' (str2 还有 'C') |
-1 | str1 < str2 |
strcmp("a", "A") |
'a'(97) > 'A'(65) | 32 | str1 > str2 (ASCII 差值) |
3. 使用注意事项
(1)不能用==
直接比较字符串
// 错误示例:比较的是地址,而非内容 if ("hello" == "world") { ... } // 比较两个字符串常量的地址(永远为假) if (str1 == str2) { ... } // 比较数组首地址(非内容)
正确做法:用
strcmp
比较内容:
if (strcmp(str1, str2) == 0) { // 内容相等 printf("两字符串相同\n"); }
(2)返回值不一定是 ±1(依赖实现)
标准仅规定返回 “正 / 负 / 零”,未规定具体数值(如strcmp("a", "A")
可能返回 32,而非 1)。
编程建议
:判断时用
>0
/
<0
/
==0
,而非固定值:
if (strcmp(s1, s2) > 0) { ... } // 正确:s1 > s2 // 错误:依赖具体返回值(如1) if (strcmp(s1, s2) == 1) { ... }
(3)必须传入合法字符串(以'\0'
结尾)
若str1
或str2
无'\0'
,strcmp
会越界比较(结果随机):
char s1[] = {'a', 'b'}; // 无'\0' char s2[] = "abc"; strcmp(s1, s2); // 越界比较(结果不可预测)
三、知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
strcat 函数 |
连接src 到dest 末尾(覆盖dest 的'\0' ,添加src 的'\0' );需dest 空间足够 |
空间计算(strlen(dest)+strlen(src)+1 );dest 和src 必须有'\0' |
⭐⭐⭐ |
strcat 常见错误 |
空间不足导致溢出;dest 无'\0' 导致连接位置错误 |
如何提前检查空间(sizeof(dest) >= ... );初始化dest 为合法字符串 |
⭐⭐⭐⭐ |
strcmp 比较规则 |
逐字符比较 ASCII 码;遇不同字符或'\0' 停止;返回正 / 负 / 零表示大小关系 |
与== 的区别(地址比较 vs 内容比较);返回值的判断(>0 而非==1 ) |
⭐⭐⭐ |
strcmp 特殊案例 |
前序字符相同但长度不同时,较短字符串更小(如 "AB" < "ABC") | 区分 “长度” 与 “字典序”(短字符串不一定小,需前序字符相同) | ⭐⭐⭐ |
综合应用 | strcpy 拷贝初始内容 + strcat 连接后续内容,构建复杂字符串 |
每次操作的空间检查;'\0' 的自动维护机制 |
⭐⭐⭐ |
四、编程建议
strcat
使用流程:// 1. 定义足够大的目标数组 char dest[100]; // 2. 初始化目标数组(确保有'\0') strcpy(dest, "初始内容"); // 3. 连接前检查空间 if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) { strcat(dest, src); // 4. 安全连接 }
strcmp
正确判断方式:int res = strcmp(s1, s2); if (res == 0) { printf("相等\n"); } else if (res > 0) { printf("s1 > s2\n"); } else { printf("s1 < s2\n"); }
替代函数(更安全):
strncat(dest, src, n)
:限制连接的最大字符数(n
),避免溢出;strncmp(s1, s2, n)
:仅比较前n
个字符,适合长字符串部分比较。
通过以上内容,可掌握strcat
(连接)和strcmp
(比较)的核心用法,理解字符串操作中 “空间管理” 和 “'\0'
维护” 的重要性,规避缓冲区溢出、比较错误等常见问题。
字符串函数(扩展):带长度限制与查找类函数详解
一、带长度限制的拷贝与连接函数
1. 字符串部分拷贝函数strncpy
(1)函数功能与核心区别
函数原型:
char *strncpy(char *dest, const char *src, size_t n);
核心功能:从源字符串
src
拷贝最多n
个字符到目标数组dest
(区别于strcpy
的 “完整拷贝”)。与
strcpy
的关键差异:特性 strcpy(dest, src)
strncpy(dest, src, n)
拷贝范围 完整拷贝(直到 src
的'\0'
)最多拷贝 n
个字符(无论src
是否结束)'\0'
处理自动拷贝 src
的'\0'
仅当 src
长度≤n
时,才用'\0'
填充剩余空间(否则不补'\0'
)目标串剩余部分 被覆盖(直到 '\0'
)未被覆盖的部分保留原值(仅替换前 n
个字符)
(2)示例与特殊行为解析
#include <stdio.h> #include <string.h> int main() { char dest[10] = "dot.com"; // 初始:d o t . c o m \0 ... char src[] = "make"; // src长度4(不含'\0') strncpy(dest, src, 4); // 拷贝src的前4个字符到dest printf("dest结果:%s\n", dest); // 输出"make.com"(前4个字符被替换,剩余保留) return 0; }
行为分析:
仅替换
dest
的前 4 个字符('d','o','t','.'
→'m','a','k','e'
);src
长度(4)等于n
(4),且src
无'\0'
("make"
的'\0'
未被拷贝),但dest
原有'\0'
在第 7 位('m','a','k','e','.','c','o','m','\0'
),因此可正常输出。
(3)使用注意事项
'\0'
可能缺失:若src
长度≥n
,拷贝后dest
前n
个字符无'\0'
(需手动添加,否则不是合法字符串)。char dest[5], src[] = "hello"; strncpy(dest, src, 4); // dest为'h','e','l','l'(无'\0') dest[4] = '\0'; // 手动添加结束符(必须!)
空间仍需足够:目标数组
dest
长度至少为n
(否则拷贝时越界)。短源字符串的填充:若
src
长度<n
,剩余位置会自动用'\0'
填充(如src
长度 2,n=5
,则拷贝 2 个字符后补 3 个'\0'
)。
2. 字符串部分连接函数strncat
(1)函数功能与使用
函数原型:
char *strncat(char *dest, const char *src, size_t n);
功能:在
dest
的末尾追加src
的前n
个字符(或src
的全部字符,以较短者为准),自动添加'\0'
。与
strcat
的区别:strcat
追加完整src
,strncat
可限制追加长度(避免溢出)。示例:
char dest[20] = "Hello"; char src[] = "World!"; strncat(dest, src, 3); // 追加src的前3个字符"Wor" printf("结果:%s\n", dest); // 输出"HelloWor"(自动添加'\0')
(2)关键特性
自动添加
'\0'
:无论追加多少字符,最终dest
都会以'\0'
结尾(安全)。目标空间要求:
dest
总长度需≥strlen(dest) + min(n, strlen(src)) + 1
(原长度 + 追加长度 +'\0'
)。
3. 字符串部分比较函数strncmp
(1)函数功能与应用
函数原型:
int strncmp(const char *str1, const char *str2, size_t n);
功能:比较
str1
和str2
的前n
个字符(区别于strcmp
的 “比较到'\0'
”)。返回值:与
strcmp
一致(正 / 负 / 零表示大于 / 小于 / 等于),但仅基于前n
个字符。典型应用:忽略字符串末尾的无关字符(如换行符)。
// 比较"quit"和"quit\n"的前4个字符(结果相等) if (strncmp(input, "quit", 4) == 0) { printf("检测到退出指令\n"); }
(2)注意事项
若
n
大于两字符串的长度,比较到较短字符串的'\0'
时停止(与strcmp
逻辑一致)。适用于 “前缀匹配” 场景(如判断字符串是否以特定前缀开头)。
二、忽略大小写比较函数strcasecmp
函数原型:
int strcasecmp(const char *str1, const char *str2);
功能:比较两个字符串,忽略字母大小写(如
'A'
与'a'
视为相等)。应用场景:用户输入容错(如 “QUIT”“Quit”“quit” 均视为相同指令)。
示例:
if (strcasecmp(input, "quit") == 0) { printf("退出程序\n"); // 匹配"quit"、"QUIT"、"Quit"等 }
注意:非字母字符(如数字、符号)仍按 ASCII 码严格比较(如
'!'
与'!'
相等,'1'
与'2'
不等)。
三、字符与子串查找函数
1. 字符查找函数strchr
与strrchr
(1)strchr
:查找字符首次出现位置
函数原型:
char *strchr(const char *s, int c); // c为字符的ASCII码(如查找'a'可传97或'a')
功能:在字符串
s
中查找字符c
首次出现的位置,返回该位置的地址;未找到返回NULL
。示例:
char s[] = "hello world"; char *p = strchr(s, 'o'); // 查找首个'o' if (p != NULL) { printf("找到字符,位置:%ld\n", p - s); // 输出4(下标从0开始) printf("从该位置开始的字符串:%s\n", p); // 输出"o world" }
(2)strrchr
:查找字符最后一次出现位置
函数原型:
char *strrchr(const char *s, int c);
功能:查找字符c在s中最后一次出现的位置区别于
strchr
的 “首次”。
char s[] = "hello world"; char *p = strrchr(s, 'o'); // 查找最后一个'o' printf("位置:%ld\n", p - s); // 输出7("world"中的'o')
(3)关键技巧
计算下标:找到的地址与字符串首地址的差值即为字符下标(
p - s
)。查找
'\0'
:strchr(s, '\0')
会返回s
末尾'\0'
的地址(可用于计算字符串长度:strchr(s, '\0') - s
等价于strlen(s)
)。
2. 子串查找函数strstr
(1)函数功能与应用
函数原型:
char *strstr(const char *haystack, const char *needle);
功能:在长字符串
haystack
中查找子串needle
首次出现的位置(如在 “hello world” 中查找 “lo”)。返回值:找到则返回子串首字符地址;未找到返回
NULL
。示例:
char str[] = "ho are you"; char sub[] = "are"; char *p = strstr(str, sub); // 查找"are"在str中的位置 if (p != NULL) { printf("子串位置:%ld\n", p - str); // 输出4(从0开始计数) printf("子串及后续:%s\n", p); // 输出"are you" }
(2)使用注意事项
子串为空:若
needle
是空字符串(""
),返回haystack
的首地址(标准规定)。区分大小写:如需忽略大小写,使用
strcasestr
(非标准但多数编译器支持)。空指针检查:未找到时返回
NULL
,使用前必须判断(否则访问NULL
会崩溃)。
三、知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
strncpy |
拷贝src 的前n 个字符到dest ;若src 长≥n ,需手动加'\0' |
与strcpy 的区别(是否限制长度);'\0' 的手动添加(否则目标串不合法) |
⭐⭐⭐ |
strncat |
追加src 的前n 个字符到dest 末尾,自动加'\0' ;需目标空间足够 |
目标空间计算(原长度 + 追加长度 + 1);与strcat 的长度限制差异 |
⭐⭐ |
strncmp |
比较前n 个字符,用于前缀匹配或忽略末尾字符(如"quit" 与"quit\n" ) |
比较范围仅限前n 个字符;返回值规则与strcmp 一致 |
⭐⭐⭐ |
strcasecmp |
忽略大小写比较字符串(字母不区分大小写,符号严格比较) | 应用场景(用户输入容错);非字母字符的严格比较 | ⭐⭐ |
strchr /strrchr |
查找字符首次 / 最后一次出现位置(返回地址);通过地址差值计算下标 | 地址差值转下标(p - s );查找'\0' 的特殊用法(等价于strlen ) |
⭐⭐⭐ |
strstr |
在长串中查找子串首次出现位置(返回地址);未找到返回NULL |
子串位置计算(p - haystack );NULL 返回值的检查(避免崩溃) |
⭐⭐⭐⭐ |
四、编程实践建议
优先使用带长度限制的函数:
strncpy
、strncat
、strncmp
比无长度限制的函数更安全(减少溢出风险)。手动添加
'\0'
:使用strncpy
时,若拷贝长度等于n
且src
无'\0'
,务必手动添加(dest[n] = '\0'
)。检查返回值:查找类函数(
strchr
、strstr
)返回NULL
时,需避免访问(如if (p != NULL) { ... }
)。结合指针运算:通过 “找到的地址 - 首地址” 快速计算下标(无需循环遍历)。