当前位置: 首页 > backend >正文

《C 语言字符串操作从入门到实战(上篇):字符分类、转换及strlen/strcpy等函数详解》

目录

一. 字符分类函数

二. 字符转换函数

三. strlen的使用和模拟实现

3.1 strlen函数理解与使用示例

3.2 ⚠️ 重要注意事项

3.3 strlen返回值陷阱

3.4 strlen的模拟实现

四. strcpy的使用和模拟实现

4.1 strcpy函数理解与使用示例

4.2 ⚠️ 重要注意事项

4.3 strcpy的模拟实现

五. strcat的使用和模拟实现

5.1 strcat的理解和使用示例

​5.2 ⚠️ 重要注意事项

5.3  strcat的模拟实现

六. strcmp的使用和模拟实现

6.1 strcmp的理解和使用示例

6.2  ⚠️ 重要注意事项

6.3  strcat的模拟实现


一. 字符分类函数

字符分类函数是C/C++标准库中用于判断字符属性的函数

这些函数的使用都需要包含头文件ctype.h

这些函数的使用方法极其类似 现在我就以isupper函数 来举例使用

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <ctype.h>
int main() {char c;scanf("%c", &c);if (isupper(c)) {printf("%c是大写字母\n", c);}else{printf("%c是小写子母\n", c);}return 0;
}

运行结果如下

可以看到 我们使用了isupper函数 下面是isupper函数的介绍 其他函数类似

下面让我们进行一个小练习

练习:

 写一个代码,将字符串中的小写字符全部转为大写,其它字符不变。

#include <stdio.h>
#include <ctype.h>
int main()
{int i = 0;char arr[] = "hello WORLD";while (arr[i]){if (islower(arr[i]))arr[i] -= 32;//小写字母比大写字母的Ascll码值大32printf("%c", arr[i]);i++;}return 0;
}

运行结果如下

二. 字符转换函数

字符转换函数是C/C++标准库中用于字符处理的函数   也需要包含头文件ctype.h

这些函数主要用于字符的大小写转换。

主要字符转换函数:

  1. tolower(int c) - 将字符转换为小写

    • 如果c是大写字母(A-Z),返回对应小写字母(a-z)
    • 其他字符保持不变
  2. toupper(int c) - 将字符转换为大写

    • 如果c是小写字母(a-z),返回对应大写字母(A-Z)
    • 其他字符保持不变

上面练习中我们将小写转大写,是利用了小写字母和大写字母的Ascll码值差32的性质,现在有了转换函数,我们就可以直接使用toupper函数实现小写转大写了。代码修改如下

练习:

 写一个代码,将字符串中的小写字符全部转为大写,其它字符不变。

#include<stdio.h>
#include<ctype.h>
int main()
{int i = 0;char arr[] = "hello WORLD";while (arr[i]){if (islower(arr[i]))arr[i]=toupper(arr[i]);printf("%c", arr[i]);i++;}return 0;
}

三. strlen的使用和模拟实现

3.1 strlen函数理解与使用示例

strlen 是 C 标准库中用于计算字符串长度的函数,定义在 <string.h> 头文件中。

函数原型

size_t strlen(const char *str);

功能说明

  • 计算以空字符 '\0' 结尾的字符串的长度

  • 返回字符串中 '\0' 之前的字符个数(不包括 '\0' 本身)

  • 返回值类型: size_t - 无符号整数类型,通常用于表示大小和计数

strlen函数使用示例

#include <stdio.h>
#include <string.h>
int main() {const char* str = "Hello, World!";size_t length = strlen(str);printf("The string \"%s\" has %zu characters.\n", str, length);return 0;
}

3.2 ⚠️ 重要注意事项

1. 字符串必须正确终止

  • strlen 依赖 '\0' 判断字符串结束,未正确终止会导致 缓冲区溢出 或 未定义行为
    char s[3] = {'a', 'b', 'c'};  // 错误!缺少 '\0'
    printf("%zu\n", strlen(s));    // 未定义行为
    

2. 不能传递 NULL

  • strlen(NULL) 会崩溃,应先检查指针:
    if (str != NULL) {size_t len = strlen(str);
    }
    

3. size_t 是无符号类型

  • 不要用 int 接收返回值,否则可能截断:
    size_t len = strlen("hello");  // 正确
    int bad_len = strlen("hello"); // 可能截断(如果字符串很长)
    

4. strlen vs sizeof

函数作用示例
strlen计算字符串长度(不包括 '\0'strlen("abc") → 3
sizeof计算变量/数组的字节数(包括 '\0'sizeof("abc") → 4

3.3 strlen返回值陷阱

首先让我们观察下面一段代码

#include <stdio.h>
#include <string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "abc";if (strlen(str2) - strlen(str1) > 0){printf("str2>str1\n");}else{printf("srt2<str1\n");}return 0;
}

不出意外 应该会打印srt2<str1 但是真是如此吗 让我们运行试试

结果却恰恰相反 为什么呢? 让我们分析一下

关键问题分析

strlen返回值类型: 返回size_t(无符号整型)

无符号数减法永远不会为负,当str2str1短时,strlen(str2)-strlen(str1)会产生一个巨大的正数(回绕)因此运算结果远大于0 执行了错误的分支语句

具体回绕细节 有兴趣的可以自行了解

正确的写法应该是

if (strlen(str2) > strlen(str1) ){printf("str2>str1\n");}else{printf("srt2<str1\n");}

这样就避免了无符号整型运算导致回绕的问题

3.4 strlen的模拟实现

方法一:  计数器

#include<stdio.h>
#include<assert.h>
size_t mystrlen(const char* str)
{int count = 0;assert(str!=NULL);while (*str){count++;str++;}return count;
}
int main()
{char arr[] = "abcdefg";size_t ret = mystrlen(arr);printf("%d", ret);return 0;
}

方法二:  指针-指针 

#include<stdio.h>
#include<assert.h>
size_t mystrlen(char* str)
{assert(str != NULL);char* p = str;while (*p)//注:\0的ASCLL码值是0{p++;}return p - str;}
int main()
{char arr[] = "abcdefg";size_t ret = mystrlen(arr);printf("%d", ret);return 0;
}

方法三:  递归(不创建临时变量)

#include<stdio.h>
#include<assert.h>
size_t mystrlen(const char* str)
{assert(str != NULL);if (*str == '\0'){return 0;}else{return 1 + mystrlen(str + 1);}}
int main()
{char arr[] = "abcdef";size_t ret = mystrlen(arr);printf("%d", ret);return 0;
}

四. strcpy的使用和模拟实现

4.1 strcpy函数理解与使用示例

strcpy 是 C 标准库中用于字符串复制的函数,定义在 <string.h> 头文件中。

函数原型

char *strcpy(char *dest, const char *src);
  • 功能说明

  • 将 src 指向的字符串(包括终止的空字符 '\0')复制到 dest 指向的缓冲区
  • 返回 dest 指针

使用示例

#include <stdio.h>
#include <string.h>
int main()
{char arr1[20] = { 0 };char arr2[] = "hello";char*p=strcpy(arr1, arr2);printf("%s\n", p);printf("%s\n", arr1);//两种打印方式都传入的是字符串的首地址 并无区别return 0;
}

4.2 ⚠️ 重要注意事项

  1. 缓冲区溢出风险

    • 必须确保目标缓冲区足够大(包括存储 '\0' 的空间)
    • 错误示例:
      char dest[5];
      strcpy(dest, "Hello World"); // 缓冲区溢出
      
  2. 目标缓冲区必须可写

    • 不能拷贝到只读内存区域
    • 错误示例:
      char *dest = "Read-only"; // 字符串字面量通常存储在只读段
      strcpy(dest, "new");      // 运行时错误
      
  3. 源字符串必须合法

    • 必须以 '\0' 结尾
    • 不能是 NULL 指针
    • 错误示例:
      char src[3] = {'a','b','c'}; // 无终止符
      char dest[10];
      strcpy(dest, src);           // 未定义行为
      
  4. 不会自动截断

    • 如果源串比目标缓冲区长,必定导致溢出
    • 错误示例:
      char dest[5];
      strcpy(dest, "This is too long"); // 必然溢出

4.3 strcpy的模拟实现

#include<stdio.h>   // 标准输入输出头文件
#include<assert.h>  // 断言头文件,用于检查运行时条件// 自定义字符串拷贝函数
// 参数:arr1 - 目标字符串指针,arr2 - 源字符串指针
// 返回:目标字符串的起始地址
char* mystrcpy(char* arr1, const char* arr2)
{// 使用断言确保两个指针都不为NULL// 如果任一指针为NULL,程序会终止并报错assert(arr1 && arr2);// 保存目标字符串的起始地址,因为arr1会在拷贝过程中被修改char* p = arr1;// 拷贝过程:逐字符拷贝,包括结尾的'\0'// 这是一个经典的C语言字符串拷贝写法// 1. *arr2++ 先解引用arr2,然后arr2指针后移// 2. 将arr2的字符赋值给arr1的位置,然后arr1指针后移// 3. 整个表达式的值是赋值的字符,当遇到'\0'时循环结束while ((*arr1++ = *arr2++)){; // 空语句,所有操作都在while条件中完成}// 返回目标字符串的起始地址return p;
}int main()
{char arr1[20];          // 声明目标字符数组,分配20字节空间char arr2[] = "abcdef"; // 初始化源字符串,包含"abcdef\0"(自动添加'\0')// 调用自定义拷贝函数char* ret = mystrcpy(arr1, arr2);// 打印拷贝结果printf("%s", ret);      // 输出:abcdefreturn 0;
}

五. strcat的使用和模拟实现

5.1 strcat的理解和使用示例

strcat 是 C 标准库中用于字符串拼接的函数,定义在 <string.h> 头文件中。

函数原型

char *strcat(char *dest, const char *src);

功能说明

  • 将 src 字符串追加到 dest 字符串的末尾(覆盖 dest 的终止空字符)

  • 保证结果字符串以 '\0' 结尾

  • 返回 dest 指针(便于链式调用)

  • 要求 dest 必须有足够的空间容纳拼接后的结果

使用示例

#include <stdio.h>
#include <string.h>
int main() {char dest[20] = "Hello";const char* src = " World!";char*p=strcat(dest, src);printf("拼接结果: %s\n",p); // 输出: Hello World!printf("拼接结果: %s\n",dest); // 输出: Hello World!return 0;
}

5.2 ⚠️ 重要注意事项

1. 目标缓冲区必须足够大

严重性:高
问题:目标缓冲区必须能容纳源字符串和目标字符串的内容(包括终止符’\0’),否则会导致缓冲区溢出

// 危险示例
char dest[10] = "hello";
strcat(dest, " world!");  // 缓冲区溢出// 安全做法
char dest[20] = "hello";  // 确保足够空间
strcat(dest, " world!");

2. 目标字符串必须以’\0’结尾

严重性:高
问题strcat 从目标字符串的’\0’处开始追加,如果目标字符串未正确终止,会导致未定义行为

// 错误示例
char dest[10] = {'h', 'e', 'l', 'l', 'o'};  // 没有'\0'
strcat(dest, " world");  // 未定义行为// 正确做法
char dest[10] = {'h', 'e', 'l', 'l', 'o', '\0'};
strcat(dest, " world");

3. 源字符串必须以’\0’结尾

严重性:高
问题strcat 会一直读取源字符串直到遇到’\0’,如果源字符串未正确终止,会导致未定义行为

// 错误示例
char src[] = {'w', 'o', 'r', 'l', 'd'};  // 没有'\0'
char dest[10] = "hello";
strcat(dest, src);  // 未定义行为

4. 不能处理重叠的内存区域

严重性:中
问题:如果源字符串和目标字符串内存重叠,行为未定义

// 错误示例
char str[20] = "hello";
strcat(str, str + 2);  // 未定义行为

5.3  strcat的模拟实现

#include<stdio.h>
#include<assert.h>// 自定义字符串连接函数
// 参数:arr1 - 目标字符串(必须有足够空间),arr2 - 要追加的源字符串
// 返回值:返回连接后的字符串首地址
char* mystrcat(char* arr1, const char* arr2)
{// 使用断言确保两个指针都不是NULL// 如果任一指针为NULL,程序会在此处终止并报错assert(arr1 && arr2);// 保存目标字符串的起始地址,用于最后返回char* p = arr1;// 第一个while循环:找到arr1的字符串结束符'\0'的位置// 通过递增指针arr1,直到遇到'\0'(值为0)为止while (*arr1){arr1++;}// 第二个while循环:将arr2的内容追加到arr1末尾// 这里使用了赋值表达式的值作为循环条件:// 1. 先执行 *arr1 = *arr2 进行字符复制// 2. 然后判断赋值表达式的值(即复制的字符)// 3. 如果复制的字符不是'\0'(值为0),则继续循环// 4. 每次循环后指针arr1和arr2都自增// 注意:这个循环会连带arr2的结束符'\0'一起复制过去while ((*arr1++ = *arr2++)){; // 空语句,所有操作都在循环条件中完成}// 返回最初保存的目标字符串首地址return p;
}int main()
{// 目标字符串数组,必须足够大以容纳连接后的结果// 初始化内容为"hello"(自动包含'\0')char arr1[20] = "hello";// 源字符串数组,内容为"world"(自动包含'\0')char arr2[] = "world";// 调用自定义的字符串连接函数char* ret = mystrcat(arr1, arr2);// 打印连接后的字符串// 输出结果为"helloworld"printf("%s", ret);return 0;
}

六. strcmp的使用和模拟实现

6.1 strcmp的理解和使用示例

strcmp 是 C 标准库中用于比较两个字符串的函数,定义在 <string.h> 头文件中。

函数原型

int strcmp(const char *str1, const char *str2);

功能说明

  • 按字典顺序比较两个以空字符 '\0' 结尾的字符串

  • 比较是基于字符的 ASCII 值进行的

  • 返回值表示比较结果:

    • 返回 0:两个字符串相等

    • 返回 负值str1 小于 str2(按字典顺序)

    • 返回 正值str1 大于 str2(按字典顺序)

使用示例

#include <stdio.h>
#include <string.h>
int main() {const char* str1 = "apple";const char* str2 = "banana";int result = strcmp(str1, str2);if (result < 0) {printf("\"%s\" < \"%s\"\n", str1, str2);}else if (result > 0) {printf("\"%s\" >\"%s\"\n", str1, str2);}else {printf("The strings are equal\n");}return 0;
}

6.2  ⚠️ 重要注意事项

 关键注意事项

1. 字符串必须正确终止

  • 严重性: 高

  • 问题: 未终止的字符串会导致未定义行为

char s1[3] = {'a','b','c'}; // 无终止符
char s2[] = "abc";
int result = strcmp(s1, s2); // 危险!

2. NULL 指针检查

  • 严重性: 高

  • 问题: 传递 NULL 指针会导致程序崩溃

char *s1 = NULL;
char *s2 = "hello";
int result = strcmp(s1, s2); // 崩溃!

3. 区分大小写

  • 注意strcmp 是大小写敏感的

strcmp("hello", "HELLO"); // 返回非零值

6.3  strcat的模拟实现

#include<stdio.h>   // 包含标准输入输出函数库
#include<string.h>  // 包含字符串操作函数库int main()
{// 定义并初始化两个字符数组(字符串)char arr1[] = "abcdef";  // arr1包含字符串"abcdef"和自动添加的'\0'char arr2[] = "abc";    // arr2包含字符串"abc"和自动添加的'\0'// 使用strcmp比较两个字符串// strcmp函数会逐个字符比较,直到遇到不同字符或'\0'// 返回值://   >0 表示arr1大于arr2//   <0 表示arr1小于arr2//   =0 表示arr1等于arr2int ret = strcmp(arr1, arr2);// 根据比较结果输出不同信息if (ret > 0)printf("arr1 > arr2\n");  // 输出arr1大于arr2else if (ret < 0)printf("arr1 < arr2\n");  // 输出arr1小于arr2elseprintf("arr1 == arr2\n"); // 输出arr1等于arr2return 0;  // 程序正常结束
}

以上就是字符函数和字符串函数上篇的全部内容 希望能够为您提供帮助

往期回顾

《C 语言 sizeof 与 strlen 深度对比:原理、差异与实战陷阱》

《C 语言 sizeof 与 strlen 深度对比:原理、差异与实战陷阱》

《VS2022 调试实战手册:高效排查代码问题的必备攻略》

http://www.xdnf.cn/news/7896.html

相关文章:

  • 智橙云PLM上线【企业知识库】,构建企业自己的研发创新知识库!!
  • 云DNS智能解析:实现多区域部署
  • 第五章 GPT模块配置
  • 深入浅出理解时间复杂度和空间复杂度
  • 【音频】如何解析mp3文件
  • 如何从 iPhone 获取照片:5 个有效解决方案
  • Wi-Fi(无线局域网技术)
  • C++类与对象(二):六个默认构造函数(二)
  • 心联网(社群经济)视角下开源AI智能名片、链动2+1模式与S2B2C商城小程序源码的协同创新研究
  • 第13天-用BeautifulSoup解析网页数据:以百度热搜可视化为例
  • leetcode2844. 生成特殊数字的最少操作-medium
  • C语言中的弱符号 __attribute__((weak)) 的使用方法
  • C语言---内存函数
  • Axure通过下拉框选项改变,控制字段显隐藏
  • Rust 学习笔记:关于泛型的练习题
  • Switch最新 模拟器 Eden(伊甸)正式发布 替代Yuzu模拟器
  • C#面:Server.UrlEncode、HttpUtility.UrlDecode的区别
  • Python里字典的操作
  • C#语法篇 :基类子类转换,成员变化情况
  • 云蝠智能大模型呼叫动态情感共情能力上线!
  • SIGIR25-推荐论文整理
  • 面试相关的知识点
  • vue3 + vite 使用tailwindcss
  • 现代化SQLite的构建之旅——解析开源项目Limbo
  • 第17天-Pandas使用示例
  • 【SPIN】PROMELA 通道(Channels)(SPIN学习系列--8)
  • 【完整版】基于laravel开发的开源交易所源码|BTC交易所/ETH交易所/交易所/交易平台/撮合交易引擎
  • 机器学习-KNN算法
  • 为什么服务器突然变慢?从硬件到软件的排查方法
  • 论文阅读:Next-Generation Database Interfaces:A Survey of LLM-based Text-to-SQL