暑期自学嵌入式——Day05补充(C语言阶段)

发布于:2025-07-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

接续上文:暑期自学嵌入式——Day05(C语言阶段)-CSDN博客

主页点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!

主页:

一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm=1010.2135.3001.5343

Day05(补充)

字符数组和字符串:字符串逆序输出详解

一、字符串逆序输出的实现思路与方法

1. 两种核心实现思路
实现方式 核心逻辑 优缺点分析
仅逆序显示(不修改原串) 不改变字符串存储顺序,仅从最后一个有效字符倒序打印到第一个字符 优点:简单直接,不破坏原数据;缺点:未真正修改字符串(仅输出层面逆序)
原地逆序(修改原串后显示) 通过交换字符位置,将原字符串改为逆序(如 "abc"→"cba"),再正常打印 优点:真正实现字符串逆序存储;缺点:需修改原数据,逻辑稍复杂
2. 基础实现:仅逆序显示(不修改原串)
(1)核心步骤
  1. 获取输入:用getsscanf读取字符串(gets支持空格,scanf遇空格停止)。

  2. 计算有效长度:用strlen获取字符串实际长度(不含'\0')。

  3. 倒序循环输出:从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 vs sizeof

    • 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. getsscanffgets对比
函数 特点 安全隐患 / 注意事项
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=0j=len-1交换,直到i>=j);需临时变量辅助 循环终止条件(i < j而非i <= j,避免重复交换);奇 / 偶长度字符串的处理(自动兼容) ⭐⭐⭐⭐
输入函数对比 gets支持空格但不安全;scanf遇空格停止;fgets安全且支持空格 gets的溢出风险;fgets保留换行符的处理方法 ⭐⭐
字符串长度计算 strlen返回有效长度(不含'\0'),需包含<string.h>;也可手动循环计算 手动计算长度的循环逻辑(while(arr[i] != '\0') i++ ⭐⭐
数组越界预防 输入时限制长度(如fgetsN参数、scanf%19s);初始化数组避免垃圾值 gets无长度限制导致的溢出;未初始化数组的随机值打印 ⭐⭐⭐

四、编程技巧

  1. 调试技巧: 逆序逻辑出错时,可打印中间过程(如双指针交换时的ij值和数组内容):

    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);
    }
  2. 边界条件处理: 考虑特殊输入(如空字符串""、单字符"a"),确保程序不崩溃:

    • 空字符串:strlen返回 0,循环不执行(正确)。

    • 单字符:i=0j=0i < j不成立,循环不执行(无需交换,正确)。

  3. 函数封装: 将逆序逻辑封装为函数,提高复用性:

    // 原地逆序函数
    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)strlensizeof 的核心区别
对比项 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 即不等,正 / 负表示大小) ⭐⭐⭐

四、编程建议

  1. strlen 使用注意

    • 仅用于以 '\0' 结尾的字符串(字符数组无 '\0' 时,strlen 会越界读取,结果随机)。

    • 返回值是 size_t(无符号),避免与有符号整数比较(如 if (strlen(s) >= -1) 永远成立,因无符号不会为负)。

  2. sizeofstrlen 对比记忆

    • 记口诀:strlen 算 “内容长度”(有效字符),sizeof 算 “房子大小”(总内存)。

    • 示例:char s[10] = "abc"strlen=3(内容),sizeof=10(房子)。

  3. 函数学习方法: 先掌握 strlen 等基础函数的用法和原理,遇到未知函数(如 strstr 查找子串)时,学会查阅 C 语言手册(如cplusplus.com),重点关注功能、参数、返回值和注意事项。

通过以上内容,可掌握 strlen 的核心用法及字符串函数的基础认知,明确 strlensizeof 的关键区别,为后续学习字符串拷贝、比较等函数奠定基础。

字符串函数:字符串拷贝函数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. 关键使用规则(必须遵守)
  1. 目标数组空间必须足够大: 需能容纳源字符串的所有字符(包括'\0'),否则会导致缓冲区溢出(覆盖相邻内存,程序可能崩溃或输出乱码)。

    • 示例:源字符串"hello"(长度 5,含'\0'共 6 字节),目标数组至少需 6 字节(如char dest[6])。

  2. 源字符串必须以'\0'结尾strcpy通过'\0'判断拷贝结束,若源字符串无'\0',会越界拷贝(直到意外遇到'\0')。

  3. 源和目标不能重叠: 如strcpy(arr+2, arr)(目标是源的一部分)会导致拷贝结果异常(标准未定义行为)。

  4. 数组名不能直接赋值: 错误写法: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]nstrlen(src)),包括src[n](即'\0' 手动实现时需确保'\0'被拷贝(否则目标数组不是合法字符串)
目标数组要求 必须可修改(非字符串常量)、空间足够(≥strlen(src)+1 字符串常量(如"test")不能作为destconst类型不可修改)
与直接赋值的区别 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的适用场景区别 ⭐⭐⭐

四、编程建议

  1. 优先使用标准库函数: 理解strcpy原理后,实际开发中直接使用标准库版本(经过优化和测试,更可靠),无需重复造轮子。

  2. 强制检查空间: 拷贝前必须验证目标数组长度 ≥ 源字符串长度 + 1,例如:

    #define DEST_LEN 20
    char dest[DEST_LEN];
    char src[] = "example";
    ​
    if (strlen(src) + 1 <= DEST_LEN) {
        strcpy(dest, src);  // 安全
    } else {
        // 处理空间不足(如截断或提示错误)
    }
  3. 了解替代函数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)的末尾,形成新的字符串。

  • 核心逻辑

    1. dest中寻找第一个'\0'(原字符串的结尾);

    2. 从该位置开始,将src的字符(包括src'\0')逐个拷贝到dest

    3. 最终destsrc'\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)必须满足的前提条件
  1. destsrc均为合法字符串(均以'\0'结尾):

    • dest'\0'strcat会越界寻找结尾(结果随机);

    • src'\0',会越界拷贝(覆盖dest后续内存)。

  2. dest空间必须足够大: 所需空间 = strlen(dest) + strlen(src) + 1(原dest长度 + src长度 + 新'\0')。

    • 示例:dest初始长度 5("Hello"),src长度 6("World"),需至少 5+6+1=12 字节(dest定义为[20]足够)。

  3. 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. 综合应用:组合strcpystrcat

通过先拷贝、再连接,可构建复杂字符串:

// 示例:构建"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 码值逐字符比较两个字符串,返回比较结果。

  • 比较规则

    1. 从第一个字符开始,逐个比较对应位置的字符(str1[i] vs str2[i]);

    2. 若遇到不同字符,返回两者 ASCII 码的差值(str1[i] - str2[i]);

    3. 若所有字符相同,比较长度:

      • 长度相同(均到'\0'):返回 0(相等);

      • 长度不同(一个先到'\0'):返回 “较长字符串 - 较短字符串” 的差值(通常简化为 ±1)。

  • 返回值约定

    • >0str1 > str2str1在字典序中更靠后);

    • =0str1 == str2(完全相同);

    • <0str1 < str2str1在字典序中更靠前)。

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'结尾)

str1str2'\0'strcmp会越界比较(结果随机):

char s1[] = {'a', 'b'};  // 无'\0'
char s2[] = "abc";
strcmp(s1, s2);  // 越界比较(结果不可预测)

三、知识小结

知识点 核心内容 考试重点 / 易混淆点 难度系数
strcat 函数 连接srcdest末尾(覆盖dest'\0',添加src'\0');需dest空间足够 空间计算(strlen(dest)+strlen(src)+1);destsrc必须有'\0' ⭐⭐⭐
strcat 常见错误 空间不足导致溢出;dest'\0'导致连接位置错误 如何提前检查空间(sizeof(dest) >= ...);初始化dest为合法字符串 ⭐⭐⭐⭐
strcmp 比较规则 逐字符比较 ASCII 码;遇不同字符或'\0'停止;返回正 / 负 / 零表示大小关系 ==的区别(地址比较 vs 内容比较);返回值的判断(>0而非==1 ⭐⭐⭐
strcmp 特殊案例 前序字符相同但长度不同时,较短字符串更小(如 "AB" < "ABC") 区分 “长度” 与 “字典序”(短字符串不一定小,需前序字符相同) ⭐⭐⭐
综合应用 strcpy拷贝初始内容 + strcat连接后续内容,构建复杂字符串 每次操作的空间检查;'\0'的自动维护机制 ⭐⭐⭐

四、编程建议

  1. strcat 使用流程

    // 1. 定义足够大的目标数组
    char dest[100];
    // 2. 初始化目标数组(确保有'\0')
    strcpy(dest, "初始内容");
    // 3. 连接前检查空间
    if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) {
        strcat(dest, src);  // 4. 安全连接
    }
  2. strcmp 正确判断方式

    int res = strcmp(s1, s2);
    if (res == 0) {
        printf("相等\n");
    } else if (res > 0) {
        printf("s1 > s2\n");
    } else {
        printf("s1 < s2\n");
    }
  3. 替代函数(更安全)

    • 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)使用注意事项
  1. '\0'可能缺失:若src长度≥n,拷贝后destn个字符无'\0'(需手动添加,否则不是合法字符串)。

    char dest[5], src[] = "hello";
    strncpy(dest, src, 4);  // dest为'h','e','l','l'(无'\0')
    dest[4] = '\0';         // 手动添加结束符(必须!)
  2. 空间仍需足够:目标数组dest长度至少为n(否则拷贝时越界)。

  3. 短源字符串的填充:若src长度<n,剩余位置会自动用'\0'填充(如src长度 2,n=5,则拷贝 2 个字符后补 3 个'\0')。

2. 字符串部分连接函数strncat
(1)函数功能与使用
  • 函数原型

    char *strncat(char *dest, const char *src, size_t n);
  • 功能:在dest的末尾追加srcn个字符(或src的全部字符,以较短者为准),自动添加'\0'

  • strcat的区别strcat追加完整srcstrncat可限制追加长度(避免溢出)。

  • 示例

    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);
  • 功能:比较str1str2n个字符(区别于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. 字符查找函数strchrstrrchr
(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返回值的检查(避免崩溃) ⭐⭐⭐⭐

四、编程实践建议

  1. 优先使用带长度限制的函数strncpystrncatstrncmp比无长度限制的函数更安全(减少溢出风险)。

  2. 手动添加'\0':使用strncpy时,若拷贝长度等于nsrc'\0',务必手动添加(dest[n] = '\0')。

  3. 检查返回值:查找类函数(strchrstrstr)返回NULL时,需避免访问(如if (p != NULL) { ... })。

  4. 结合指针运算:通过 “找到的地址 - 首地址” 快速计算下标(无需循环遍历)。


网站公告

今日签到

点亮在社区的每一天
去签到