C中常见的字符函数和字符串函数讲解
本文重点(前言)
介绍我们在平常编写代码时常见的字符串,以及怎么用c语言怎么实现并且该如何使用。
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数.
常见字符串的分类
- 求字符串的长度 - - -(strlen)
- 长度不受限制的字符串函数 - - -(strcpy strcat strcmp)
- 长度受限制的字符串函数介绍- - -(strncpy strncat strncmp)
- 字符串查找- - -(strstr strtok)
- 错误信息的报告- - -(strerror)
- 字符操作
- 内存操作函数- - -(memset memmove memcpy memcpy)
函数介绍
怎么在哪找参考资料
我们在学习这些字符函数光凭自己的知识储备是远远不够的,我们需要借鉴一些资料,我们可以在www.cpluscplus.com网站去查找。
对于这个网站现在更新了,变成了新版,如果喜欢用旧版的话可以点击右上角的[Legacy version]
函数介绍
(1)strlen
<1>strlen函数介绍
strlen的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
由名字就可以看出strlen是由string+length组成的,意思就是求字符串长度,统计的就是字符串中
\0
前的出现的字符个数,对于一个字符串"hello"
用双引号括住,包括h e l l o \0
这6个字符,\0
用于结束一个字符串,而再用strlen
计算字符串的大小不算\0
这个字符。
有人问如果是中文怎么办对于c语言来说,其实一个中文占两个字符,知道这个就行了
strlen
怎么用
-
const char* str
是被const
修饰的char
型不可修改的指针 -
size_t
.可能有的同学不认识这个东西不妨打开vs编译器,对这个 转到定义查看,就能一目了然。用我们已知的知识可以知道typedef
是重命名,将无符号整型重命名为size_t
所以strlen
的返回值是无符号整型。
strlen
的参数
strlen
的返回值
-
关于
- 对于strlen的返回值是无符号整型
strlen
的返回值的例题
#include <stdio.h>
#include<string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
对于上述这道题,乍一看,可能觉得
strlen("abc")
是3,而strlen("abcdef")
是6,3 - 6是 -3,小于0,但是我们要记住是strlen
的返回值是无符号整型,没有负数,自动转换为整数所以正确答案是 >
<2>strlen函数的模拟实现
-
strlen函数的模拟实现代码
- 用指针-指针的方法实现strlen函数代码如下(对于相同类型的数据来说,按照c语言规定来说指针-指针就是元素个数)
#include<assert.h>
#include<stdio.h>
size_t my_strlen(const char* str)//用const修饰char*,表示不让修改str,让代码更健壮
{
assert(str);//断言函数,防止str是空指针。
const char* start = str;
const char* end = str;
while (*end!='\0')
{
end++;
}
return end-start;
}
int main()
{
char arr[] = "abcdef";
printf("len = %d\n", my_strlen(arr));
return 0;
}
(2)strcpy
<1>strcpy
的函数介绍
strcpy
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
- 由名字就可以看出strcpy是由string+copy组成的,意思就是字符串拷贝.
-
char * destination, const char * source
,可以看出有两个参数,从英文可以看出,一个是目的地,一个是源头,就是从源头拷贝到目的地。对于源头参数来说,是不可修改的所以用const修饰。 -
char *
strcpy的返回值是char*,返回一个字符指针。 -
Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point). -
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
请找出下面代码的问题
- 代码如下
strcpy
函数是干什么的
strcpy
函数的参数是什么
strcpy
函数的返回值是什么
strcpy
怎么用
#include<stdio.h>
#include<string.h>
int main()
{
char arr1 = "hello people";
char arr2[20] = "abcedef";
//将arr2的内容拷贝到arr1中
printf("%s\n", strcpy(arr1, arr2));
//第一个参数是目的地,第二个是源头。
//因为返回值是char*所以可以链式访问
return 0;
}
对于arr1来说,
"hello people"
是一个常量字符串,对于常量字符串来说是不可修改的。而我们知道strcpy
的第一个参数是目的地是可修改的。
<2>strcpy
的函数模拟实现
- 代码如下
strcpy
模拟实现的代码
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);//断言,防止是空指针
char* ret = dest;
while (*ret++ = *src++)//就是拷贝,拷贝到\0的时候停下来。
{
;
}
return dest;//返回目的地的起始地址。
}
int main()
{
char arr1[20] = "hello people";
char arr2[20] = "abcedef";
//将arr2的内容拷贝到arr1中
printf("%s\n", my_strcpy(arr1, arr2));
//第一个参数是目的地,第二个是源头。
//因为返回值是char*所以可以链式访问
return 0;
}
(3)strcat
<1>strcat
的函数介绍
strcat
的参数和返回值可以在[www.cpluscplus.com](strcpy
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:)上查询 ,如图:
-
这个从名字看不出来,但是可以看上述网站上对于该函数的功能介绍。
Appends a copy of the source string to the destination string. The terminating null characterin destination is overwritten by the first character of source, and a null-character is includedat the end of the new string formed by the concatenation of both in destination.
其实就是字符追加函数,比如arr1[20]="hello";``arr2[20]="world";
就可以用这个函数追加成"hello world"
-
char * destination, const char * source
,可以看出有两个参数,从英文可以看出,一个是目的地,一个是源头,就是从源头追加到目的地。对于源头参数来说,是不可修改的所以用const修饰。 -
char *
strcat的返回值是char*,返回一个字符指针。 -
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
追加是从源字符串后\0
后追加。
strcat
函数是干什么的
strcat
函数的参数是什么
strcat
函数的返回值是什么
strcat
怎么用
<2>strcat
的函数模拟实现
- 代码如下
strcat
模拟实现的代码
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);//断言,防止是空指针
char* ret = dest;
while (*dest)//读取到目的地的\0的位置
{
*dest++;
}
while (*dest++ = *src++)//追加
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "world";
printf("%s\n", my_strcat(arr1, arr2));//链式访问
return 0;
}
-
用
-
其实是不可以的,他有一定的缺陷,加入
arr1[20]="we"
自己给自己追加w覆盖\0,e覆盖后面的\0,当原本的\0,没有了变为了w,就让这个不能够停下来,所以用自己追加自己时不要用strcat自己追加自己。
strcat
模拟实现的代码思考一个问题,能不能自己给自己追加?
(4)strcmp
<1>strcmp
的函数介绍
strcpy
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
This function starts comparing the first character of each string. If they are equal to each
other, it continues with the following pairs until the characters differ or until a terminating
null-character is reached.
由名字就可以看出strcmp是由string+compare组成的,意思就是字符串比较. -
const char * str1, const char *str2
,可以看出有两个参数,从英文可以看出,是两个比较的参数。对于这两个比较的参数来说,都是是不可修改的所以用const修饰。 -
int
,strcmp的返回值是int,返回一个整型变量。 -
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
比较对应位置的字符,比较他们的ASCⅡ值,如果相同就比较下一个,直到分出大小。
画图解释
-
请看下图
strcmp
函数是干什么的
strcmp
函数的参数是什么
strcmp
函数的返回值是什么
strcmp
函数怎么比较
返回小于1的数字,说明arr1小于arr2,也说明我们的解释正确
<2>strcmp
的函数模拟实现
- 代码如下
strcmp
模拟实现的代码
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);//断言
while (*str1 == *str2 )//判断两个字符是否相等,如果相等,两个加分别+1,
{
if (*str1 == '\0')//如果都为\0那么两个字符串相等
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;//直接相减,为ascⅡ值相减,正负就是大小的表示
}
int main()
{
char arr1[20] = "abcdef";
char arr2[20] = "abq";
printf("%d\n", my_strcmp(arr1, arr2));//链式访问
return 0;
}
小总结
我们上面这些函数
strcmp
,strcpy
,strcat
,这些函数都必须依赖\0
,都看\0
来进行他们的功能
所以我们叫这些函数是长度不受限制的字符串,不关注对几个字符进行拷贝,追加,和比较。
因此还存在一些函数是长度受限制的字符串,能对几个字符进行拷贝,追加,和比较。
比如strncmp
,strncpy
,strncat
,下面我们就介绍这些函数。
(5)strncpy
<1>strncpy
的函数介绍
strncpy
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
我们由上面的strcpy函数可知这是字符串拷贝函数,但是比他多了一个n,是长度受限制的字符串函数,比如:拷贝2个字符,不需要看
\0
。
其拷贝方法与strcpy
相同. -
char * destination, const char * source, size_t num
,我们可以看出他对于strcpy多了一个size_t num
参数,其他参数相同,而这个参数就是控制要拷贝几个字符。n是几,就表示拷贝几个字符。 -
strncpy的返回值是
char*
,返回一个字符指针。
strncpy
函数是干什么的
strncpy
函数的参数
strncpy
函数的返回值是什么
(6)strncat
<1>strncat
的函数介绍
strncat
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
我们由上面的strcat函数可知这是字符串追加函数,但是比他多了一个n,是长度受限制的字符串函数,比如:追加2个字符,不需要看
\0
。
其追加方法与strcat
相同. -
char * destination, const char * source, size_t num
,我们可以看出他对于strcpy多了一个size_t num
参数,其他参数相同,而这个参数就是控制要追加几个字符。n是几,就表示追加几个字符。 -
strncat
的返回值是char*
,返回一个字符指针。
用 -
其实是可以的,追加后,自己在最后自动补上
\0
,因此如果自己给自己追加,就用strncat
函数.
strncat
函数是干什么的
strncat
函数的参数
strncat
函数的返回值是什么
strncat
的库函数思考上面一个问题,能不能自己给自己追加?
(7)strncmp
<1>strncmp
的函数介绍
strncmp
的参数和返回值可以在[www.cpluscplus.com](strncmp
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:)上查询 ,如图:
-
我们由上面的
strcmp
函数可知这是字符串追加函数,但是比他多了一个n,是长度受限制的字符串函数,比如:比较2个字符,不需要看\0
。
其追加方法与strcmp
相同. -
const char * str1, const char * str2, size_t num
,我们可以看出他对于strcmp
多了一个size_t num
参数,其他参数相同,而这个参数就是控制要比较几个字符。n是几,就表示比较几个字符。 -
strncmp
的返回值是int
,返回一个整型。
strncmp
函数是干什么的
strncmp
函数的参数
strncmp
函数的返回值是什么
可以看出,比较前三个字符,发现相同,返回值就是0,并且他也是一个一个字符比较。
(8)strstr
<1>strstr
的函数介绍
strstr
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
其实这个函数很好理解,就是在一个字符串里找另一个字符串,看他是否存在于这个字符串中。
比如:在"abcdefabcdef"
字符串中找到"fab"
字符串,就用strstr
函数
如果存在,返子串第一次出现的起始地址,
不存在,返回NULL -
const char * str1, const char * str2
,因为是查找字符串,所以两个字符串都不能修改。所以都用const修饰。在第一个字符串中找第二个字符串。 -
strstr
的返回值是char *
,返回一个字符型指针。
strstr
函数是干什么的
strstr
函数的参数
strstr
函数的返回值是什么
<2>strstr
的函数模拟实现
- 代码如下
strstr
模拟实现的代码
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);//断言
char* p = str1;
char* s1 = str1;
char* s2 = str2;
while (*p)
{
s1 = p;
s2 = str2;
while (*s1!='\0'&&*s2!='\0'&& * s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return p;//找到字串
}
p++;
}
return NULL;//找不到字串
}
int main()
{
char arr1[20] = "abcdefabcdef";
char arr2[20] = "fab";
char* p= my_strstr(arr1, arr2);
if (p == NULL)
{
printf("没找到字符串。\n");
}
else
{
printf("%s\n", p);
}
return 0;
}
(9)strtok
<1>strtok
的函数介绍
strtok
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
这个函数有点不好理解,其实可以理解成字符串分割函数,将一个字符串通过分割符分割成几个单个的字符,从而得到这几个单个的字符,但是需要注意的是使用
strtok
会改变原字符串,所以一般先将字符串拷贝,然后将拷贝的字符串进行分割。
比如:现在又一个字符串123456@QQ.com
,分隔符是'@和.'
,就可以使用这个函数将字符串分割成123456
,QQ
,com
,三个字符串。
但是需要注意的一点就是:再找第二个分割字符串时,他已经将第一个字符串的分割符变为\0
,这样打印时就能直接答应,所以在分割第二个字符串时,传空指针直到找到下一个分割符。
不动就看下面代码有注释。 -
char * str, const char * delimiters
,第一个字符指针指向是要被分割的字符的地址。而第二个字符指针指向分隔符的地址。 -
strtok
的返回值是char *
,返回一个字符型指针,返回是分割字符串的起始位置。
strtok
函数是干什么的
strtok
函数的参数
strtok
函数的返回值是什么
但是这个方法有点挫,你必须知道分割几回,再写每回分割的代码。那有没有好一点的代码呢,能够直接将字符串分割。那肯定有呀。
这样通过一个循环就能够知道能分割几个字符串了。这样就很智能。
-
需要注意的几点
-
1.sep参数是个字符串,定义了用作分隔符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回 NULL 指针。
(10)strerror
<1>strerror
的函数介绍
strerror
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
返回错误码,所对应的错误信息。我们平常上网遇到的404就是错误码。
这个错误码就是c语言自己规定的
请看下图就可以明白。
-
int errnum
这个就是错误码,通过错误码来看到是错了什么。 -
char *
我们从上图可以知道错误信息是一个字符串,通过字符指针来找到这个字符串 -
我们要先看了解
errno
,errno
就是将错误信息记录到错误码里的东西。
补充 -
perror
,也是打印错误信息的函数其实相当于printf+strerror
.
strerror
函数是干什么的
strerror
函数的参数
strerror
函数的返回值是什么
strerror
函数的应用场景
字符分类函数:
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母az或AZ |
isalnum | 字母或者数字,az,AZ,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
这些函数都比较简单,可以直接操作,不懂可以上上面的网站上自己看看,他非常简单。
(11)memcpy
<1>memcpy
的函数介绍
memcpy
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
-
其实这个函数很好理解,可以从字面上理解,memory+copy,就是内存拷贝函数,也是内存函数,这时有的同学就会问,那这和strcpy函数有什么区别呢,其实我们可以通过函数的参数就能明白区别在哪了。memcpy的函数参数是
void*
类型,说明他什么都能拷贝,而strcpy不一样,只能拷贝字符串。 -
void * destination, const void * source, size_t num
因为是拷贝,源头参数都不能修改,所以用const修饰。
size_t
就是拷贝 多少字节个数,这个很重要。如果拷贝一个整型,那么就是4个字节。 -
memcpy
的返回值是void*
,因为不知道返回的是什么类型所以我们用void*
类型接收。
memcpy
函数是干什么的
memcpy
函数的参数
memcpy
函数的返回值是什么
<2>memcpy
的函数模拟实现
- 代码如下
memcpy
模拟实现的代码
void* my_memcpy(void* dest, void* src, size_t num)
{
assert(dest && src);//断言
char* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
//因为char*是一个字节一个字节读取,所以我们强制转换为char*类型
dest = (char*)dest + 1;
//因为dest是void*类型所以可以接受任何类型,将dest强制类型转换为char*+1,就很合理
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 20);
//拷贝20个字节也就是5个整形。
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d\n", arr2[i]);
}
return 0;
}
}
但是如果我们思考一下如果自己拷贝自己几个字符,你觉得用上面这个代码合适吗。
显然这个是不太合适的。当我们自己拷贝自己时,可能会改变我们需要拷贝的字符,进而我们最后拷贝的字符就变成了拷贝完的字符,下来同学们自己思考思考应该怎么做。
所以memcpy
一般是不能拷贝重叠的字符,但是在vs编译器下,比较智能,可以实现但是在其他编译器上就有可能不会实现
因此就又有一个函数memmove
,内存移动函数,就可移动重叠的字符。
(12)memmove
<1>memmove
的函数介绍
memmove
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
- 其实这个函数很好理解,可以从字面上理解,memory+move,就是内存移动函数,也是内存函数,
-
void * destination, const void * source, size_t num
因为是只是移动,源头参数都不能修改,所以用const修饰。
size_t
就是移动 多少字节个数,这个很重要。如果移动一个整型,那么就是4个字节。 -
memmove
的返回值是void*
,因为不知道返回的是什么类型所以我们用void*
类型接收。
注意
-
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove
函数处理。
memmove
函数是干什么的
memmove
函数的参数
memmove
函数的返回值是什么
(19)memcmp
<1>memcmp
的函数介绍
memcmp
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
- 其实这个函数很好理解,可以参考memcpy,相当于字符串比较的拓展,就是对于什么都可以比较,也是内存函数,比较方法和strcmp比较类似。
-
const void * ptr1, const void * ptr2, size_t num
因为是只是比较,两个参数都不能修改,所以用const修饰。
size_t
就是移动 多少字节个数,这个很重要。如果比较一个整型,那么就是4个字节。 -
memcpy
的返回值是void*
,因为不知道返回的是什么类型所以我们用void*
类型接收。
memcmp
函数是干什么的
memcmp
函数的参数
memmove
函数的返回值是什么
(12)memset
<1>memset
的函数介绍
memset
的参数和返回值可以在www.cpluscplus.com上查询 ,如图:
- 其实这个函数很好理解,memset,就是内存设置函数,将指定的内存设置为指定的内存。
-
void * ptr, int value, size_t num
,int value
就是要将内存设置成什么类型。
size_t
就是设置 多少字节个数,这个很重要。如果设置一个整型,那么就是4个字节。 -
memset
的返回值是void*
,因为不知道返回的是什么类型所以我们用void*
类型接收。
memset
函数是干什么的
memset
函数的参数
memset
函数的返回值是什么
本文总结
本文主要就是介绍字符串函数和内存设置函数的使用和模拟实现,不过就都是九牛一毛,希望大家可以举一反三,明白怎么学习库函数,网站也给大家了,方法也交给大家啦,我相信大家的学习能力,以后遇到自己没见过的库函数一定可以自己解决,希望大家一键三联,一起加油!!!!😊😊