C库函数:string.h

news2024/11/28 3:15:11

string.h

C 标准库 – <string.h> | 菜鸟教程 (runoob.com)

1void *memchr(const void *str, int c, size_t n)
在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。

在这个函数中,可以看到有void *作为形参,void *作为返回值。

这代表什么呢?

void *作为形参表示可以传入任意类型的指针;如果是返回值,则表示可以转成任意类型的指针。但是,通常情况下,输入输出的都是字符指针,传入其他类型的指针无操作意义。

输入时可以直接传入,但是输出时通常需要将void *强转成其他所需类型的指针。因为函数输出的结果总要是一个具体的类型。

这里的位置,指的就是目标字符所在的地址,即指针。

#include <stdio.h>
#include <string.h>
 
int main ()
{
    const char str[] = "be.happy.everyday";
    const char ch = 'p'; //虽然第二个参数是int,但是可以直接传入char
    char *ret;
 
    ret = (char *)memchr(str, ch, strlen(str));
 
    printf("result is %s\n", ret);
	printf("result is %c\n", *ret);
 
    return(0);
}

运行结果:

2int memcmp(const void *str1, const void *str2, size_t n)
把 str1 和 str2 的前 n 个字节进行比较。

你还在用‘>’‘<’‘=’等比较字符串吗?

事实上,用大于小于比较字符串的方法是不对的。我们看一下两种常见的错误方法。

#include<stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
 
	if (arr1 < arr2)
	{
		
	}
 
	if ("abcdef" < "abc")
	{
 
	}
}

为什么两个字符串不能直接用大于小于号比较?
因为用大于小于号,比较的是字符串的首地址大小,而不是字符串的大小。如上述例子,比较的是"abcdef"中a的地址和"abc"中a的地址。

为什么不能用数组名直接比较
因为数组名代表首元素地址(一般情况下),用数组名比较跟用字符串直接比较性质是一样的,所以用数组名比较也是不对的。


字符串比较大小的实质

比较字符串的大小就是比较每个字符串中对应位置上的字符大小(ASC II码值大小),如果相同,就比较下一对,直到不同或者都遇到'\0'。  

3void *memcpy(void *dest, const void *src, size_t n)
从 src 复制 n 个字符到 dest

该函数返回一个指向目标存储区 str1 的指针,即返回结果是指向目标字符串。

我有个疑问,复制后是接续还是覆盖?

直接说结论:是覆盖。

通常,dest是一个未初始化的字符数组。

 ​​​​​​​

 

4void *memmove(void *dest, const void *src, size_t n)
另一个用于从 src 复制 n 个字符到 dest 的函数。

memmove和memcpy的作用都是内存拷贝,唯一的区别是,当内存发生局部重叠时:memmove保证了拷贝的结果是正确的,但是memcopy不一定是正确的。

但是memcpy比memmove速度快。

考虑下面两种内存重叠的情况:

  • 第一种情况下,拷贝重叠的区域不会出现问题,内容均可以正确的被拷贝。
  • 第二种情况下,问题出现在右边的两个字节,这两个字节的原来的内容首先就被覆盖了,而且没有保存。所以接下来拷贝的时候,拷贝的是已经被覆盖的内容,显然这是有问题的。

注意:是一个字符一个字符地复制的,所以第二种情况会出错。

在实际使用时,使用memmove是比memcpy更安全的。

更多参考:

memmove 和 memcpy的区别_nguliu的博客-CSDN博客_memmove

碰到个问题,我刚想把两个函数操作放一起比较,结果发现结果不对,原来是我两次都是基于原数据来计算的,事实上,运行第一个函数后,内存中的数据就已经变了。

#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "123456789";
	char *p1 = s;
	char *p2 = s+2;
	
	char *rp1 = (char *)memcpy(p2, p1, 5); //运行一次后,原来的数据已经变了
	char *rp2 = (char *)memmove(p2, p1, 5); //第二次运行是在第一次基础上操作的
	printf("Hello, World! %s\n", rp1);
	printf("Hello, World! %s\n", rp2);
   
    return 0;
}
5void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

复制到前n个字符,还是第n个字符?

#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "behappy";
	
	printf("Hello, World! %s\n", memset(s, 'a', 4));
   
    return 0;
}

注意,该值返回一个指向存储区 str 的指针。输入时str是什么类型的指针,输出的就是什么类型的指针,这里不必特意强制类型转换。

memset用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化。

6char *strcat(char *dest, const char *src)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

该函数返回一个指向最终的目标字符串 dest 的指针。

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src -- 指向要追加的字符串,该字符串不会覆盖目标字符串。

以上提到了两个关键点,一是目标数组要包含一个字符串,二是要用足够的容量来容纳。

经过测试,我们发现strcat的实现模式是将src中的所有字符(连同字符串最后的’\0’一起)加到dest字符串中第一个‘\0’的位置。

    

值得注意的是:
1.追加的字符串src和目标字符串dest中都必须要带有字符’\0’,并且追加字符串src必须以‘\0’结尾,否则追加过程无法顺利实现。
2.目标字符串空间必须足够大(足够容纳下追加字符串src的内容)。
3.目标空间必须可修改(前面不能加const并且不能说常量字符串)

但是,在进行测试的时候,发现了一些问题。

1、

直接定义字符串可以追加。理论分析,s并没有足够的容量去容纳。但确实是追加上了。

#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "behappy";
	char t[] = "everyday";
	
	printf("%s\n", strcat(s, t)); //behappyeveryday
   
    return 0;
}

2、

目标字符串未初始化,且容量不足容纳源字符串,但还是追加上了

#include <stdio.h>
#include <string.h>

int main()
{
    char s[1];
	char t[] = "everyday";
	
	printf("%s\n", strcat(s, t)); //everyday
   
    return 0;
}

这是什么原因?

因为strcat函数是不安全的。然数组s的长度是1,但将t连接到s的后面时,不会进行越界检查,而是直接将s追加到t的后面。这样,就会占用不属于s的内存,所以运行程序时可能出现多种情况,比如:
1.
程序奔溃
2.
表现正常
3.
没奔溃,但程序出现莫名其妙的现象

这3种可能都有可能出现,出现2实属侥幸,说不定下次运行时就会出现情况1或3。

最好是保证第一个空间长度足够,不足也会尝试继续往后放第二个字符串的字符,但是这种做法是十分危险的,可能会覆盖掉一些有用的数据,导致程序的不可预知的错误,也可能是尝试往一个可读不可写的存储空间写入数据则程序直接崩溃。

先要肯定一点:教程没有说错!  你在编程时必须要注意这一点:保证strcat的第一个参数有足够的空间!为什么没报错呢? 因为:这个函数的参数是指针类型,函数中也只是通过指针来读写这些内存,编译器无从得知这几块内存到底多大,所以编译阶段编译器不会报错。函数的行为大致就是先找到第一个参数所指的内存中字符串结尾的位置,然后从此处开始依次写入第二个参数中的字符...直到写完。函数也根本不知道第一个参数所指的内存空间到底够不够大,所以函数本身也不会对此检查。而在运行时,如果向第一个参数处写入了过多的字符,则有可能会引起问题,也有可能不会:假如第一个参数所指的内存空间后面的内存刚好也是可读写的,那么函数就继续向其中写入字符,虽然这已经是算写“越界”了,但函数不知道啊。 这样后面输出时就会把这字符串完整地输出。但假如第一个参数所指内存后面不可写,到函数写越界时就会引起运行时错误。所以这样的代码在很多时候不会触发运行时错误而运行出看似正确的结果,但这是非常错误的代码!  即便后续的内存是可写的,但也许其中存着其他数据,写越界就有可能覆盖这些数据,引起其他bug,这是严重的安全隐患! 其实所谓“缓冲区溢出漏洞”大致就是这样来的。

7char *strncat(char *dest, const char *src, size_t n)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

n字符长度是指追加后整个的长度,还是指src要追加的长度?

#include <stdio.h>
#include <string.h>

int main()
{
    char d[20] = "be happy";
	char s[9] = " everyday";
	
	printf("%s\n", strncat(d, s, 5)); //be happy ever
   
    return 0;
}

经测试,n是指要源字符串要追加的字符长度。

strncat和strcat有何区别?

因为strcat是以'\0'作为结束标志的,所以strcat无法完成对自身的追加。

  

问题原因:在第一次循环的时候就自身的‘\0’就已经被第一个元素覆盖了,导致后面找不到'\0'所以无法退出循环,进入了死循环中。

这时候,就需要用到strncat,strncat的原理不同于strcat,strcat是利用'\0'来退出循环,strncat则是用长度来退出,因为有长度的限制,所以只要长度达到了就能结束,安全性有了更好的保证。

8char *strchr(const char *str, int c)
在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。

该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。

和memchr相比,strchr不限制前n个字符了,而是在整个目标字符串中搜索。

memchr检测的是一段内存,strchr检测的是一个字符串,如果一段内存中有\0字符作为有效数据的话,显然不能用strchr去查找的。

strchr会停在\0,memchr不会。

strchr期望第一个参数以空值终止,因此不需要长度参数。 memchr的工作方式类似,但不期望内存块以空值终止,因此可以成功搜索\0字符。

9int strcmp(const char *str1, const char *str2)
把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
10int strncmp(const char *str1, const char *str2, size_t n)
把 str1 和 str2 进行比较,最多比较前 n 个字节。

该函数返回值如下:

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str1 大于 str2。
  • 如果返回值 = 0,则表示 str1 等于 str2。
12

char *strcpy(char *dest, const char *src)
把 src 所指向的字符串复制到 dest

需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。

13

char *strncpy(char *dest, const char *src, size_t n)
把 src 所指向的字符串复制到 dest,最多复制 n 个字符。

当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

可用于给某声明过的数组进行初始化。

或者给某个数组整个地改变其内容。

#include <stdio.h>
#include <string.h>

int main()
{
	char s[20] = "everydayyyy";
	char t[] = "behappy";
	
	printf("%s\n", strcpy(s, t)); //behappy
   
    return 0;
}

直接复制到s,不管s里面有什么内容。

strcpy()函数是将一个字符串复制到另一块空间地址中的函数,‘\0’是停止拷贝的终止条件,同时也会将 '\0' 也复制到目标空间。

16size_t strlen(const char *str)
计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

   

18char *strrchr(const char *str, int c)
在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。

strchr是从第一次出现的位置,strrchr是最后一次出现的位置。注意区分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用docker安装zabbix监控

官网手册&#xff1a;docker安装zabbix 首先需要安装好docker服务 创建专用于Zabbix组件容器的网络 docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 zabbix-net启动空的 MySQL 服务器实例 docker run --name mysql-server -t \ //mysql容器…

FFMPEG完美入门资料---002---FFmpeg 支持能力说明

FFMPEG入门资料---001---介绍和参数说明_音视频开发老马的博客-CSDN博客 接着上文写&#xff1a; 2.3.1 FFmpeg 对编码解码器的支持 ffmpeg 支持的编解码器种类共有 280 多种&#xff0c; 涵盖了几乎所有常见音视频编码格式&#xff0c; 能解码几乎所有的音视频&#xff0c; …

月交付破万,长安深蓝成造车新势力“头部玩家”

近日&#xff0c;造车新势力纷纷公布2022年12月交付数据。通过数据&#xff0c;我们发现众多造车新势力都取得了不错的成绩&#xff0c;“头部玩家”的门槛甚至提升至万辆。与此同时&#xff0c;“排位”形势也与往年大相径庭&#xff0c;其中最令人意外的是2022年发布的深蓝品…

ESP8266+MicroPython开发:使用ESP8266+Thonny烧录MicroPython固件

使用ESP8266Thonny烧录MicroPython固件下载固件下载安装Thonny在Thonny烧录固件测试Thonny简单使用下载固件 MicroPython下载网址 根据ESP8266的flash选择&#xff0c;一般选择如图所示 自己随便选择一个版本下载&#xff0c;注意记住自己的下载路径 下载安装Thonny Thonn…

【vue系列-05】vue的生命周期(详解)

深入理解vue的生命周期一&#xff0c;vue的生命周期1&#xff0c;创建流程1.1&#xff0c;beforeCreate1.2&#xff0c;created2&#xff0c;挂载流程2.1&#xff0c;beforeMount2.2&#xff0c;mounted3&#xff0c;更新流程3.1&#xff0c;beforeUpdate3.2&#xff0c;update…

ConvNeXt V2论文翻译:ConvNeXt V2与MAE激情碰撞

文章目录摘要1 简介2 相关工作3 全卷积掩码自编码器4 全局响应归一化5 ImageNet实验6 迁移学习实验7 结论摘要 论文链接&#xff1a;ConvNeXt V2 在改进的架构和更好的表示学习框架的推动下&#xff0c;视觉识别领域在21世纪20年代初实现了快速现代化和性能提升。例如&#xf…

评价类模型(层次分析法与模糊评价模型)

一、评价类模型 综合评价的基本理论和数据预处理&#xff1a; 评价对象评价指标权重系数综合评价模型评价者 二、AHP法—层次分析法 通过打分解决评价类问题&#xff0c;两两比较&#xff0c;推算权重。 %function RIAHPRI(n) %利用MATLAB求随机一致性指标; i0;CI0;Azer…

pyqt5下的QInputDialog跟QFileDialog以及关闭主窗口子窗口自动关闭

QInputDialog 具体的参数可以参考&#xff1a; Qt&#xff1a;30---QInputDialog标准输入对话框_mb6128aabee41d4的技术博客_51CTO博客 官网连接&#xff1a; QInputDialog Class | Qt Widgets 5.15.12 这里只介绍QInputDialog.getText 代码实现&#xff1a; from PyQt5.…

【从零开始学爬虫】采集食品行业最新报价数据

l 采集网站 【场景描述】采集食品行业最新报价数据。 【源网站介绍】中国报告大厅网休闲食品行业数据频道提供休闲食品行业数据信息&#xff0c;在此有大量休闲食品行业数据信息可供选择&#xff0c;是一个可以帮助休闲食品行业了解数据的平台。 【使用工具】前嗅ForeSpider…

hbase2.x orphan regions on filesystem

问题描述&#xff1a;orphan regions on filesystem 可以通过主master web页面的HBCK Report查看 也可以通过hbck2工具查看 # 查看指定表 hbase hbck -j $HBASE_HOME/lib/hbase-hbck2-1.3.0-SNAPSHOT.jar addFsRegionsMissingInMeta default:tableName # 查看命名空间下所有…

Git分支开发中的问题

前言我们在开发中&#xff0c;经常是很多人开发同一份代码&#xff0c;早期没有git工具的时候那可真是噩梦&#xff0c;要复制来复制去&#xff0c;不止繁琐&#xff0c;还容易出错&#xff0c;所以后来涌现了各种代码工具&#xff0c;Svn&#xff0c;Git等等&#xff0c;而Git…

一、mysql基础、MySQL的安装及卸载、DML、DQL

MySQL基础 1、数据库相关概念 以前我们做系统&#xff0c;数据持久化的存储采用的是文件存储。存储到文件中可以达到系统关闭数据不会丢失的效果&#xff0c;当然文件存储也有它的弊端。 假设在文件中存储以下的数据&#xff1a; 姓名 年龄 性别 住址 张三 23 男 北京西三…

干货|app自动化测试之Andriod WebView如何测试

Hybrid App&#xff08;混合模式移动应用&#xff09;是介于 Web-app、Native-app 之间的 app&#xff0c;本质上是 Native-app 中嵌入 WebView 组件&#xff0c;在 WebView 组件里可以访问 Web App。Hybrid App 在给用户良好交互体验的同时&#xff0c;还具备了 Web App 的跨平…

Python数学建模问题总结(2)数据可视化Cookbook指南【源自Google可视化团队】

今天跟大家分享一套谷歌数据可视化团队形成的全面的数据可视化指南&#xff0c;涵盖了设计原则、图表分类、图表的选用、样式设计、交互设计、仪表板设计等方面。一、可视化问题不论你是从事数据相关工作&#xff0c;还是业务相关工作&#xff0c;或多或少都会需要用到数据可视…

docker 部署maven服务器用于项目的部署和发布

书接上文 现状: 已经搭建好了maven远程服务器,并将自己的项目从自己的pc部署到maven远程服务器.假设了kie-server集群可以每个node都连接这个远程服务器 要做的: 1.创建kie-server集群的时候设置环境变量. 2.看是否能连上自己搭建的maven服务器.3.如果2 ok 把kie-server需要…

干货|app自动化测试之Appium 原理 与 JsonWP 协议分析

想要使用 Appium 进行测试&#xff0c;那么就一定要先了解Appium的原理。Appium 不仅能够实现移动端的 JSONWP&#xff0c;并且延伸到了 Selenium 的 JSONWP&#xff0c;它能够控制不同移动设备的行为&#xff0c;例如通过会话安装和卸载 APP。Appium 原理图中依赖关系解释&…

MySQL高级 【视图】

目录 1&#xff1a;视图 1.1&#xff1a;介绍 1.2&#xff1a;语法 1.3&#xff1a;检查选项 1.4&#xff1a;视图的更新 1.5&#xff1a;视图作用 1.6&#xff1a;案例 1&#xff1a;视图 1.1&#xff1a;介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。…

一文看懂什么是「低代码」!低代码真的能取代程序员吗?

早在数年前&#xff0c;亚马逊、微软、阿里、腾讯等国内外巨头公司就已纷纷入局“低代码”赛道&#xff0c;而很多人连低代码是什么&#xff0c;究竟有什么用都不太知道。 本文就从概念讲起&#xff0c;结合三个生动的行业案例&#xff0c;一文讲通“什么是低代码”。 文章有…

【强训】Day03

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、选择二、编程1. 字符串中找出连续最长的数字串2. 数组中出现次数超过一半的数字答案1. 选择2. 编程普通小孩也要热爱生活&#xff01; 一、选择 以下代码运行输出的是&#xff08;&#xff09; public class …

都2023了,为什么选择Nacos,这篇文章让你入门Nacos

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 Nacos入门 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f649;。 ♨️如果文章有什么…