文章目录
- 0、总结
- 1、字符分类、转换函数
- 2、strlen的使用和模拟实现
-
- [2.1 strlen的使用](#2.1 strlen的使用)
- [2.2 strlen的模拟实现](#2.2 strlen的模拟实现)
- 3、strcpy的使用和模拟实现
-
- [3.1 strcpy的使用](#3.1 strcpy的使用)
- [3.2 strcpy的模拟实现](#3.2 strcpy的模拟实现)
- 4、strcat的使用和模拟实现
-
- [4.1 strcat的使用](#4.1 strcat的使用)
- [4.2 strcat的模拟实现](#4.2 strcat的模拟实现)
- 5、strcmp的使用和模拟实现
-
- [5.1 strcmp的使用](#5.1 strcmp的使用)
- [5.2 strcmp的模拟实现](#5.2 strcmp的模拟实现)
- 6、strncpy函数的使用
- 7、strncat函数的使用
- 8、strncmp函数的使用
- 9、strstr的使用和模拟实现
-
- [9.1 strstr的使用](#9.1 strstr的使用)
- [9.2 strstr的模拟实现](#9.2 strstr的模拟实现)
- 10、strtok函数的使用
- 11、strerror函数的使用
0、总结

在编程的过程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,我们要学习一下这些函数。
1、字符分类、转换函数
C语言中有一系列的函数是专门做字符分类的,也就是说,一个字符是属于什么类型的字符。这些函数的使用都需要包含一个头文件是ctype.h
,分类函数参考:https://cplusplus.com/reference/cctype/。
常用的分类函数如下:
isspace
:空白字符,https://cplusplus.com/reference/cctype/isspace/isdigit
:十进制数字,https://cplusplus.com/reference/cctype/isdigit/islower
:小写字母a~z,https://cplusplus.com/reference/cctype/islower/isupper
:大写字母A~Z,https://cplusplus.com/reference/cctype/isupper/
转换函数的头文件也是ctype.h
,参考如下:
tolower
:大写转小写,https://cplusplus.com/reference/cctype/tolower/toupper
:小写转大写,https://cplusplus.com/reference/cctype/toupper/
示例,写一个代码,将字符串中的小写字母转大写,其他字符不变。
c
#include <stdio.h> /* printf */
#include <ctype.h> /* islower、toupper */
#include <string.h> /* strlen */
int main()
{
char str[] = "hello,world";
int len = strlen(str);
int i = 0;
for (i = 0; i < len; i++)
{
if (islower(str[i])) str[i] = toupper(str[i]);
}
printf("%s\n", str);
return 0;
}
2、strlen的使用和模拟实现
2.1 strlen的使用
介绍:
- 头文件:
string.h
- 函数原型:
size_t strlen(const char* str);
- 作用:计算字符串的长度
- 参考:https://cplusplus.com/reference/cstring/strlen/
总结如下:
- 字符串以
'\0'
,作为结束标志,strlen
函数返回的是在字符串中'\0'
前面出现的字符个数,不包含'\0'
。 - 参数指向的字符串必须要以
'\0'
结束。 - 注意函数的返回值为
size_t
,是无符号的(易错)。
2.2 strlen的模拟实现
方式一:计数器方式
c
#include <stdio.h> /* printf */
#include <assert.h>
int my_strlen(const char* str)
{
int count = 0;
// 如果str是空指针,程序会输出一条错误的信息并终止执行。
// 因为如果直接对空指针进行解引用,会导致程序崩溃。
assert(str);
// 循环一直执行,直到str指向的字符是空字符\0。
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
const char* str = "abc";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
方式二:不能创建临时变量计数器
c
int my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
方式三:指针-指针的方式
c
int my_strlen(const char* str)
{
assert(str);
char* p = str;
while (*p)
p++;
return p - str;
}
3、strcpy的使用和模拟实现
3.1 strcpy的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strcpy(char* destination, const char* source);
- 作用:拷贝字符串到目标空间
- 参考:https://cplusplus.com/reference/cstring/strcpy/
总结如下:
- 源字符串必须以
'\0'
结束。 - 会将源字符串中的
'\0'
拷贝到目标空间。 - 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可修改。
3.2 strcpy的模拟实现
题目出自《高质量C/C++编程》书籍最后的试题部分
c
#include <stdio.h> /* printf */
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest);
assert(src);
// 赋值后的结果(即*src的值作为条件)
// 当*src为'\0'时候,条件为假,循环结束。
// 就是说,先获取*src的值,再将这个值赋给*dest,用这个值(即*src的值)作为循环的条件。
while (*dest++ = *src++);
return ret;
}
int main()
{
char a[30] = "xxxxxxxx";
char b[4] = "abc";
printf("复制前:%s\n", a);
my_strcpy(a, b);
printf("复制后:%s\n", a);
return 0;
}
解释while (*dest++ = *src++);
,如下:

4、strcat的使用和模拟实现
4.1 strcat的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strcat(char* destination, const char* source);
- 作用:追加目标字符串
- 参考:https://cplusplus.com/reference/cstring/strcat/
总结如下:
- 源字符串必须以'\0'结束。
- 目标字符串中也得有'\0',否则没办法知道追加从哪里开始。
- 目标空间必须有足够的大,且必须可修改。
- 字符串自己给自己追加,如何?
4.2 strcat的模拟实现
c
#include <stdio.h> /* printf */
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest);
assert(src);
while (*dest) dest++;
while (*dest++ = *src++);
return ret;
}
int main()
{
char a[30] = "xxxxxxxx";
char b[4] = "abc";
printf("复制前:%s\n", a);
my_strcat(a, b);
printf("复制后:%s\n", a);
return 0;
}
my_strcat
函数在将字符串自己追加自己时会导致无限循环,可能引发程序崩溃。
5、strcmp的使用和模拟实现
5.1 strcmp的使用
介绍:
- 头文件:
string.h
- 函数原型:
int strcmp(const char* str1, const char* str2);
- 作用:比较字符串的大小
- 参考:https://cplusplus.com/reference/cstring/strcmp/
总结如下:
- 第一个字符串大于第二个字符串,则返回大于0的数字。
- 第一个字符串等于第二个字符串,则返回0。
- 第一个字符串小于第二个字符串,则返回小于0的数字。
- 是如何判断两个字符串呢?比较两个字符串中对应位置上字符ASCII码值的大小。
5.2 strcmp的模拟实现
c
#include <stdio.h> /* printf */
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
// if (*str1 == '\0') 和 if (*str2 == '\0') 在逻辑上是等价的,
// 因为当两个字符串相等时,它们会同时到达末尾(即遇到'\0')。
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[] = "abq";
char arr2[] = "abcdef";
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
6、strncpy函数的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strncpy(char* destination, const char* source, size_t num);
- 作用:拷贝num个字符从源字符串到目标空间
- 参考:https://cplusplus.com/reference/cstring/strncpy/
注意:如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
7、strncat函数的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strncat(char* destination, const char* source, size_t num);
- 作用:将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个
\0
字符。 - 参考:https://cplusplus.com/reference/cstring/strncat/
注意:如果source指向的字符串的长度小于num的时候,只会将字符串中到\0的内容追加到destination指向的字符串末尾。
8、strncmp函数的使用
介绍:
- 头文件:
string.h
- 函数原型:
int strncmp(const char* str1, const char* str2, size_t num);
- 作用:比较str1和str2的前num个字符
- 参考:https://cplusplus.com/reference/cstring/strncmp/
9、strstr的使用和模拟实现
9.1 strstr的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strstr(const char*, const char*);
- 作用:函数返回字符串str2在字符串str1中第一次出现的位置。
- 参考:https://cplusplus.com/reference/cstring/strstr/
注意:字符串的比较匹配不包含\0
字符,以\0
作为结束标志。
9.2 strstr的模拟实现
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
char* my_strstr(const char* str1, const char* str2)
{
char* cp = (char*)str1;
char* s1, * s2;
if (!*str2)
return (char*)str1;
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && !(*s1 - *s2))
s1++, s2++;
if (!*s2)
return cp;
cp++;
}
return NULL;
}
int main()
{
char s1[] = "aaabbbcccdefbbcdefa";
char s2[] = "ccdef";
printf("%s\n", my_strstr(s1, s2));
return 0;
}
10、strtok函数的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strtok(char* str, const char* delimiters);
- 作用:分割字符串
- 参考:https://cplusplus.com/reference/cstring/strtok/
总结如下:
- delimiters参数指向一个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用
\0
结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。) - strtok函数的第一个参数不为
NULL
,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。 - strtok函数的第一个参数为
NULL
,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 - 如果字符串中不存在更多的标记,则返回
NULL
指针。
示例:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "192.168.6.111";
char* sep = ".";
char* str = NULL;
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
c
运行:
192
168
6
111
11、strerror函数的使用
介绍:
- 头文件:
string.h
- 函数原型:
char* strerror(int errnum);
- 作用:将错误码转换为对应的错误信息字符串地址。
- 参考:https://cplusplus.com/reference/cstring/strerror/
总结如下:
函数能返回对应错误码的错误信息字符串地址。系统和 C 语言标准库规定了一些错误码,一般在头文件中说明。程序启动时用变量记录当前错误码,初始为 0 表示无错误。调用标准库函数出错时,会将对应错误码存入因错误码是整数较难理解,所以每个错误码都有对应的错误信息,函数就能返回该错误信息字符串的地址。
打印0~10这些错误码对应的信息
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i = 0;
for (i = 0; i <= 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
c
在Windows11+VS2022环境下输出运行:
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
No such device or address
Arg list too long
Exec format error
Bad file descriptor
No child processes
举个例子:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pFile;
pFile = fopen("1.txt", "r");
if (pFile == NULL)
printf("Error:%s\n", strerror(errno));
return 0;
}
c
运行:
Error:No such file or directory
也可以了解perror函数,总结如下:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pFile;
pFile = fopen("1.txt", "r");
if (pFile == NULL)
perror("Error 1.txt");
return 0;
}
c
运行:
Error 1.txt: No such file or directory
所以,perror函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。
完。