一篇博客学会系列(1) —— C语言中所有字符串函数以及内存函数的使用和注意事项
访客 发布于2023-10-06 办公教程 105
目录
1、求字符串长度函数
1.1、strlen
2、字符串拷贝(cpy)、拼接(cat)、比较(cmp)函数
2.1、长度不受限制的字符串函数
2.1.1、strcpy
2.1.2、strcat
2.1.3、strcmp
2.2、长度受限制的字符串函数
2.2.1、strncpy
2.2.2、strncat
2.2.3、strncmp
3、字符串查找函数
3.1、strstr
3.2、strtok
4、错误信息报告函数
4.1、strerror
4.2、perror
5、字符函数
5.1、字符分类函数
5.2、字符转换函数
5.2.1、tolower
5.2.2、toupper
6、内存操作函数
6.1、memcpy
6.2、memmove
6.3、memset
6.4、memcmp
1、求字符串长度函数
1.1、strlen
- strlen用于求字符串长度。
- 包含头文件<string.h>。
- 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
- 参数指向的字符串必须要以 '\0' 结束。
注意:
1、函数的返回值为size_t,是无符号的( 易错 )
2、因为strlen返回的是 '\0' 前面的字符个数,如果字符串中间本身就一个'\0',那么返回的值就会返回字符串中的'\0'之前的字符个数。
例如:"abc\0def" 这个字符串,使用strlen函数会返回3。
【使用方式】
int main()
{
char arr[] = "Hello hacynn";
int ret = strlen(arr);
printf("%d\n", ret);
return 0;
}
【运行结果】
【易错提醒】
请问ret的值是多少?
int ret = strlen("abc") - strlen("abcdef");
答案是3,因为函数的返回值为size_t,是无符号的整型。
【模拟实现strlen】
int my_strlen(char* arr)
{
int count = 0;
while (*arr != '\0')
{
count++;
arr++;
}
return count;
}
int main()
{
char arr[] = "Hello hacynn";
int ret = my_strlen(arr);
printf("%d\n", ret);
return 0;
}
2、字符串拷贝(cpy)、拼接(cat)、比较(cmp)函数
2.1、长度不受限制的字符串函数
2.1.1、strcpy
- strcpy用于拷贝字符串,将字符串2拷贝到字符串1当中。
- 包含头文件<string.h>。
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
【使用方法】
int main()
{
char arr1[] = "Hello hacynn";
char arr2[20] = { 0 };
strcpy(arr2,arr1);
printf("%s\n", arr2);
return 0;
}
【运行结果】
【模拟实现strcpy】
char* my_strcpy(char* dest,const char* src)
{
char* ret = dest;
while (*dest = *src)
{
dest++;
src++;
}
return ret;
}
int main()
{
char arr1[] = "Hello hacynn";
char arr2[20] = { 0 };
my_strcpy(arr2,arr1);
printf("%s\n", arr2);
return 0;
}
2.1.2、strcat
- strcat用于拼接两个字符串,将字符串2拼接到字符串1末尾。
- 包含头文件<string.h>。
- 源字符串必须以 '\0' 结束(保证找得到目标空间的末尾),在拷贝时会把源字符串的 '\0 '也拷贝过去。
- 目标空间必须有足够的大,能容纳下源字符串的内容,并且还可以被修改。
注意:
不能字符串自己追加自己,因为当自己追加自己的时候,追加的过程中会将目标字符串的 '\0' 覆盖掉,而有因为此时目标字符串就是源字符串,就会导致源字符没有 '\0' ,将会一直拼接下去导致死循环。
虽然有些环境中该函数可以完成自己拼接自己,但是C语言的标准中并未规定strcat可以自己拼接自己,所以这个函数最好不要使用在自己拼接自己的情况下。如果真有自己追加自己的场景,建议使用strncat函数,这个函数将在下文进行讲解。
【使用方式】
int main()
{
char arr1[20] = "Hello ";
char arr2[] = "hacynn" ;
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
【运行结果】
【模拟实现strcat】
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
//找到目标空间的末尾
while (*dest != '\0')
{
dest++;
}
//数据追加
while (*dest = *src)
{
dest++;
src++;
}
return ret;
}
int main()
{
char arr1[20] = "Hello ";
char arr2[] = "hacynn" ;
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
2.1.3、strcmp
- strcmp用于比较两个字符串。
- 包含头文件<string.h>。
- 误区:该函数不是比较字符串长度的,而是比较对应位置上字符的大小(ASCII)。
- 标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
【使用方式】
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abz";
if (strcmp(arr1, arr2) > 0)
printf(">\n");
else if (strcmp(arr1,arr2) < 0)
printf("<\n");
else
printf("=\n");
return 0;
}
【运行结果】
【模拟实现strcmp】
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else
return -1;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abz";
if (my_strcmp(arr1, arr2) > 0)
printf(">\n");
else
printf("<=\n");
return 0;
}
2.2、长度受限制的字符串函数
- 就是可以限制操作个数的字符串函数。
- 包含头文件<string.h>。
2.2.1、strncpy
- 区别仅与strcpy差一个参数,记录要操作的个数。
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
- 因为拷贝个数由用户自己决定,因此\0没有被拷贝过来的可能性也是有的。
【使用方式】
int main()
{
char arr1[] = "Hello hacynn";
char arr2[20] = { 0 };
strncpy(arr2, arr1, 5); //拷贝前五个字符 ,此时拷贝\0后arr2中并不会有\0
printf("%s\n", arr2);
return 0;
}
【运行结果】
【特殊情况】
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。如下:
int main()
{
char arr1[] = "Hello";
char arr2[20] = "xxxxxxxxxxxxxxxxx";
strncpy(arr2, arr1, 10); //此时10大于arr1的元素个数,就会在后添加0直至够10个
printf("%s\n", arr2);
return 0;
}
2.2.2、strncat
- 区别也仅与strcat差一个参数,记录要操作的个数。
- 使用strncat追加,当结束追加时,就算没到\0,也会在末尾追加一个\0。
- 如果源字符串的长度小于num,则追加完源字符串之后,会自动停止追加。注意此处与strncpy的区别。
- 包含头文件<string.h>。
【使用方式】
int main()
{
char arr1[20] = "Hello ";
char arr2[] = "hacynn" ;
strncat(arr1, arr2, 3);
printf("%s\n", arr1);
return 0;
}
【运行结果】
2.2.3、strncmp
- 区别也仅与strcmp差一个参数,记录要操作的个数。
- 包含头文件<string.h>。
【使用方式】
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcz";
if (strncmp(arr1, arr2, 3) > 0) //只比较前三个字符
printf(">\n");
else if (strncmp(arr1, arr2, 3) == 0)
printf("=\n");
else
printf("<\n");
return 0;
}
【运行结果】
3、字符串查找函数
3.1、strstr
- 查找一个字符串中是否存在与另一个字符串当中,即找子串。
- 返回一个指向str1中第一个出现str2的指针,如果str2不是str1的一部分,则返回一个空指针NULL。
- 包含头文件<string.h>。
【使用方式】
可以看到,即使是有两个字串 ,也只会返回第一次出现的地址。
int main()
{
char arr1[] = "abcdefghidef"; //def出现了两次
char arr2[] = "def";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
printf("找不到\n");
else
printf("%s\n", ret);
return 0;
}
【运行结果】
【模拟实现strstr】
const char* my_strstr(const char* str1, const char* str2)
{
if (*str2 == '\0')
return str1;
char* pc = str1; //pc用于记录开始匹配的位置
while (*pc)
{
char* s1 = pc; //遍历str1指向的字符串
char* s2 = str2; //遍历str2指向的字符串
while (*s1 && *s2 && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
return pc;
pc++;
}
return NULL;
}
int main()
{
char arr1[] = "abcdefghidef";
char arr2[] = "def";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
printf("找不到\n");
else
printf("%s\n", ret);
return 0;
}
【图解】
3.2、strtok
比较奇葩的一个函数
char * strtok ( char * str, const char * delimiters );
- 切割字符串函数,例如hacynn@nash.com,当切割标记是@和 . 时,通过三次合理的使用可以切割出三个字符串:hacynn nash com
- 包含头文件<string.h>。
- delimiters参数是个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针
【使用方式】
int main()
{
char arr[] = "hacynn@nash.com";
char buf[200] = { 0 }; //因为strtok会改变被操作字符串,
//所以拷贝一个临时变量来操作
strcpy(buf, arr);
char* p = "@.";
char* s = strtok(buf, p); //参数不为NULL,找到第一个标记
printf("%s\n", s);
s = strtok(NULL, p); //参数为NULL,找到下一个标记
printf("%s\n", s);
s = strtok(NULL, p); 参数为NULL,找到下一个标记
printf("%s\n", s);
return 0;
【运行结果】
【使用方式优化 】
在实际开发中,我们不一定知道这个字符串是怎样的,这个字符串需要切割几次的,因此手动设置切割几次将代码写死的方式是不可取,而应该使用以下的方式进行自动切割。
int main()
{
char arr[] = "hacynn@nash.com.hahaha@abcd";
char buf[200] = { 0 };
strcpy(buf, arr);
char* p = "@.";
char* s = NULL;
for (s = strtok(buf, p); s != NULL; s = strtok(NULL, p))
{
printf("%s\n", s);
}
return 0;
}
这里巧妙的运用了for函数的初始化部分只执行一次的特点,而strtok也只需要第一次传地址,其他时候都只需要传NULL就行。
【优化后的运行结果】
4、错误信息报告函数
4.1、strerror
- strerror函数是将错误码翻译成错误信息,返回错误信息的字符串起始地址。
- 包含头文件<string.h>。
- C语言中使用库函数的时候,如果发生错误,就会将错误码放在errno的变量中,errno是一个 全局变量,可以直接使用。
【错误码举例】
int main()
{
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("%d: %s\n", i, strerror(i));
}
return 0;
}
每一个错误码都对应一个错误信息
【使用方式】
以打开文件为例子,fopen以读的形式打开文件,当文件存在时打开成功,文件不存在时打开失败,并返回空指针。可以利用这个来设置一个打开失败时的错误信息告知。
int main()
{
FILE* pf = fopen("add.txt", "r"); //当前文件路径中并没有add.txt文件,打开失败
if (pf == NULL)
{
printf("打开文件失败,原因是:%s\n", strerror(errno));
return 1;
}
else
{
printf("打开文件成功\n");
}
return 0;
}
【运行结果】
4.2、perror
- perror也是用于翻译错误信息 ,但与strerror不同的是,perror会直接打印错误码所对应的错误信息。而perror中传递的字符串参数就是自定义显示信息的部分,打印的结果就是 自定义显示信息:错误信息
- 包含头文件<stdlib.h>
- 可以简单理解为:perror = printf + strerror 即翻译又打印
【使用方式】
int main()
{
FILE* pf = fopen("add.txt", "r");
if (pf == NULL)
{
perror("打开文件失败"); //注意:此处是perror,不是printf。
return 1;
}
else
{
printf("打开文件成功\n");
}
return 0;
}
【运行结果】
5、字符函数
5.1、字符分类函数
字符分类函数使用非常简单,由于篇幅受限,在这里不就一一列举了 ,只需要把下面的图看懂就行。
5.2、字符转换函数
5.2.1、tolower
这个函数听名字就知道是用于将大写字母转换成小写字母,而这类函数唯一需要注意的就是函数有返回值,返回类型为int,因此在使用的时候最好使用一个int ret接收返回值。
int main()
{
int ret = tolower('A');
printf("%c\n", ret);
}
5.2.2、toupper
小写字母转大写字母,其他注意点与tolower一致。
6、内存操作函数
上文讲到的字符串函数只适用于字符串,但是内存中的数据不仅仅只有字符,这就导致这些函数有很大的局限性。因此需要有一个能够对所有类型的数据都适用的函数,这就是内存操作函数的出现的原因。下面我们来学习一下内存操作函数。
6.1、memcpy
- 函数memcpy从source的位置开始向后拷贝num个字节的数据到destination的内存位置。
- 包含头文件<string.h>
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。
- 因为C语言标准中并未规定memcpy能适用于重叠内存的拷贝,因此不重叠内存的拷贝才使用memcpy,而重叠内存的拷贝使用接下来讲解的memmove函数。
【使用方式】
使用memcpy拷贝整型数据。
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5 };
memcpy(arr1, arr2, sizeof(int) * 5);
int i = 0;
for ( i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
【运行结果】
【模拟实现memcpy】
void* my_memcpy(void* dest, const void* src, size_t sz)
{
void* ret = dest;
while (sz)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
sz--;
}
return ret;
}
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5 };
my_memcpy(arr1, arr2, sizeof(int) * 5);
int i = 0;
for ( i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
6.2、memmove
- memmove的参数和功能与memcpy完全一致。
- 包含头文件<string.h>
- 唯一有区别的就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 因此当出现重叠内存的拷贝时,就使用memmove函数处理。
【模拟实现memmove】
void* my_memmove(void* dest, const void* src, size_t sz)
{
void* ret = dest;
if (dest < src)
{
while (sz)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
sz--;
}
}
else
{
while (sz--)
{
*((char*)dest + sz) = *((char*)dest + sz);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5 ,6,7,8,9,10 };
my_memmove(arr1, arr1+2, sizeof(int) * 5);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
6.3、memset
- 将ptr所指向空间的前num个字节设置为指定值value。
- 包含头文件<string.h>
【使用方式】
int main()
{
char arr[] = "hello world";
memset(arr + 6, 'x', 3);
printf("%s\n", arr);
return 0;
}
【运行结果】
6.4、memcmp
- 比较ptr1和ptr2前num个字节的内容。
- 包含头文件<string.h>
- 标准规定:
ptr1大于ptr2,则返回大于0的数字。
ptr1等于ptr2,则返回0。
ptr1小于ptr2,则返回小于0的数字。
【使用方式】
int main()
{
int arr1[] = { 1,2,3,4,5,6,7 };
int arr2[] = { 1,2,3,7 };
int ret = memcmp(arr1, arr2, sizeof(int) * 3);
printf("%d\n", ret);
}
【运行结果】
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
更多 推荐PPT模板
-
一份简洁大气的商务风格工作总结汇报PPT模板,浅灰色背景,蓝色主色调,包括过渡页、时间轴、折线图、地图等页面。
2023-06-14 765
-
灰格子背景 简洁蓝商务PowerPoint幻灯片模板
2023-06-15 752
-
不忘初心 牢记使命——蓝金党政风工作汇报ppt模板
2023-06-15 743
-
彩虹多彩王国ppt模板
2023-06-15 739
相关推荐
- 10-06 K8sGPT,基于 AI 的云原生终极工具
- 10-06 【Java】复制数组的四种方式
- 10-06 Qt::图层框架-图片图层-序列图层-QGraphicsPixmapItem
- 10-06 一篇博客学会系列(1) —— C语言中所有字符串函数以及内存函数的使用和注意事项
- 10-06 美丽的图论
- 10-06 Pygame实现黑客帝国屏幕效果
- 10-06 后台管理系统: 商品管理
- 10-06 chrome插件-入门
- 10-06 【C++入门到精通】C++入门 —— set & multiset (STL)
- 10-02 【数据结构】什么是数据结构?
- 最近发表
-
- 一份有创意的大学生求职个人简历PPT模板,时尚全图背景配合大号字体,共32页,内容非常详尽,好看的动态效果。使用字体:汉仪菱心体简。
- 一份精美时尚的商务风格多功能幻灯片模板,以城市楼群远景图片为背景,全图型设计,时尚大气,共18页,目录页、过渡页、时间轴、图文说明等各类页面齐全丰富,可用于商务
- 小清新复古色系通用PPT模板。一份小清新风格的幻灯片模板,采用养眼柔和的复古色系艺术抽象条纹设计,通用性强。
- 实用精品商业计划书PPT模板。一份精美实用的商业计划策划书PPT模板,框架完整,内容实用,严谨专业,页面丰富。
- 炫酷欧美复古杂志风PPT模板。一份创意精美幻灯片模板,采用复古色系配合杂点做旧效果,很有质感,杂志风格的排版布局,无缝滚动切换效果,适合用于新年展望、员工激励等
- 一份实用的项目合作方案汇报PPT模板,排版布局配色简洁大气专业,包括项目介绍、需求分析、项目设计、各方需要配合的工作、项目预算几个部分。
- 好看的半透明效果PPT模板。一份很好看的幻灯片模板,采用时尚的半透明效果设计,大气山峰雪山背景,动态演示。
- 一份精美的简约的半透明IOS苹果风格PPT模板,渐变模糊光晕背景,半透明图形元素,好看的动态播放效果,用途广泛。
- 黑黄配色时尚欧美风PPT模板。一份精美好看的幻灯片模板,黑黄大气经典配色,时尚欧美范,共19页,页面丰富实用。
- 杂志风项目策划汇报PPT模板。经典红黑配色,干净利落的大气商务风格,动态演示,适合项目策划报告汇报使用。
- 标签列表
- 控制面板
- 搜索