目录
一. 字符分类函数
字符分类函数是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码值大32
printf("%c", arr[i]);
i++;
}
return 0;
}
运行结果如下
二. 字符转换函数
字符转换函数是C/C++标准库中用于字符处理的函数 也需要包含头文件ctype.h
这些函数主要用于字符的大小写转换。
主要字符转换函数:
tolower(int c)
- 将字符转换为小写
- 如果
c
是大写字母(A-Z),返回对应小写字母(a-z)- 其他字符保持不变
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
vssizeof
函数 作用 示例 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
(无符号整型)
无符号数减法永远不会为负,当str2
比str1
短时,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 ⚠️ 重要注意事项
缓冲区溢出风险
- 必须确保目标缓冲区足够大(包括存储
'\0'
的空间)- 错误示例:
char dest[5]; strcpy(dest, "Hello World"); // 缓冲区溢出
目标缓冲区必须可写
- 不能拷贝到只读内存区域
- 错误示例:
char *dest = "Read-only"; // 字符串字面量通常存储在只读段 strcpy(dest, "new"); // 运行时错误
源字符串必须合法
- 必须以
'\0'
结尾- 不能是 NULL 指针
- 错误示例:
char src[3] = {'a','b','c'}; // 无终止符 char dest[10]; strcpy(dest, src); // 未定义行为
不会自动截断
- 如果源串比目标缓冲区长,必定导致溢出
- 错误示例:
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); // 输出:abcdef
return 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等于arr2
int ret = strcmp(arr1, arr2);
// 根据比较结果输出不同信息
if (ret > 0)
printf("arr1 > arr2\n"); // 输出arr1大于arr2
else if (ret < 0)
printf("arr1 < arr2\n"); // 输出arr1小于arr2
else
printf("arr1 == arr2\n"); // 输出arr1等于arr2
return 0; // 程序正常结束
}
以上就是字符函数和字符串函数上篇的全部内容 希望能够为您提供帮助
往期回顾
《C 语言 sizeof 与 strlen 深度对比:原理、差异与实战陷阱》