IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言进阶 字符串与内存函数 介绍+模拟 (1) -> 正文阅读

[C++知识库]C语言进阶 字符串与内存函数 介绍+模拟 (1)

C语言对于字符以及字符串的使用非常频繁,为了方便使用,创造C语言的前辈们为我们书写了很多可以调用的函数,在这篇博客将会详细的介绍这些函数


🚩思维导图

在这里插入图片描述
可以看出,字符串函数种类繁多,我们一个一个介绍:


1. 求字符串长度函数:

strlen

strlen函数是非常常用的字符串函数,使用方法也很简单,先到MSDN中查看一下他的参数。
在这里插入图片描述
参数是一个char类型的指针,其实就是让我们传字符串的地址进去,他的介绍也很简单,就是给字符串求长度,这里有个值得注意的地方,strlen函数的返回值是一个size_t,是无符号的,这是易错点,演示一下它的使用。

int main()
{
	char a[] = "abcdefg";
	int size = strlen(a);
	printf("string length = %d \n", size);
	return 0;
}

在这里插入图片描述

那么,字符串的长度为什么是7呢,是以什么为界限划分的呢?
其实,我们书写一个字符串的时候,如果字符数组的空间还有剩余(没空间是一种错误的写法,意味着字符串没有结束标志),他会在字符串的末尾添加一个’\0’,表示字符串到这就结束了,相同的,strlen函数就是基于这一点来判断字符串的长度:
在这里插入图片描述
可以看到,这个字符数组的大小是8,长度为7,数组的最后一个元素是’\0’。
学习C语言时,最重要的是理解原理,上手操作,所以这篇博客并不仅仅只是介绍字符串函数,还要教大家一步一步的模拟实现字符串函数:
1.理解原理
strlen函数的原理很简单,就是遍历你传输进去的字符串,找到’\0’停止,并记录’\0’的之前的字符个数。
2.函数参数
函数参数直接模拟MSDN中的参数就可以了。
3.返回值
返回值的类型是size_t,是无符号的,这点要特殊注意。

size_t my_strlen(const char *str)
{
	assert(str);//首先确定str是一个非空的地址
	int cnt = 0;//计算个数
	while (*str != '\0')
	{
		cnt++;
		str++;
	}//当字符串元素不等于0时,计数器++,指针后移一位
	return cnt;//返回长度
}

这种是最简单易懂的写法,还有一起常见的写法如指针法,递归法等这里不再一一赘述,需要的同学可以参考博客


strlen函数的三种写法


我们也可以测试一下我们写的函数对不对:
在这里插入图片描述
也可以多试几个例子,大家可以回头自己试。
对于strlen函数还有一点要强调的就是一定要注意’\0’,只有结尾有’\0’的字符串才能正常的求长度。

2.长度不受限制的字符串函数

??strcpy

同样,看到一个我们不认识的函数,先到MSDN查一下:
在这里插入图片描述
这回参数是两个char * 类型的指针,一个带const,一个不带,返回值也是一个指针,再看介绍,拷贝一个字符串,可以推测,这个字符串函数的作用是那个带const的字符串拷贝到不带的那个字符串里面,至于是不是,我们可以实验一下:
在这里插入图片描述

可以看到函数成功的把b中储存的字符串覆盖到了a所在的空间中,那么这个函数的作用就清晰了,但是还有几个问题,需要解决一下,他是如何实现的?拷贝到哪里为止?拷贝的时候要不要带上’\0’?abcde后的fg是否被置空了?他们去哪里了?当遇到这些问题的时候,我们的思考就不能只浮于表面了,要深挖其中原理。
1.strcpy拷贝的是一个字符串,既然是字符串,那结尾的的标志就是’\0’,所以,拷贝到’\0’的位置就可以了,至于我原本的’\0’有没有拷贝过来,我们可以调试一下:
在这里插入图片描述
这时我们将b字符串拷贝到a的空间里面去,我们发现a[4]空间的值被赋成0了,也就是说,源字符串的’\0’被拷贝了过来,并且我们还发现一件事,a中原先有的fg’\0’并没有被删除,还在原先的空间里,所以这个函数只是单纯的把另一个字符串拷贝了过来,并没有改变目标字符串的其他位置。
2.要拷贝的字符串必须有结尾标志’\0’,如果没有这个0,我怎么知道这个字符串到哪里停止呢?
3.目标空间必须够大,不够大的话我按照步骤拷贝,超出的部分不就越界了吗?越界程序是会崩溃的。
4.目标空间必须是可变的不能是常量,例如:
在这里插入图片描述
由于’'‘abcdefg’'是一个常量字符串(为什么是一个常量在指针进阶提到过,其实就是先创建"abcdefg"放到静态区再把它首地址给a),存储在静态区,不能被改变,自然,程序就崩溃了。
可以看到,strcpy函数需要注意的地方有很多,但是只要我们理解其中原理,实践一下,记住也就不是事了
模拟实现

函数的返回类型是 char*,参数是char *const char*

在这里插入图片描述
假如我们的指针一位一位的后移,在后移的过程中把src所指向的值赋给det,那么strcpy函数就完成了
,具体代码应该怎么写呢?

char* my_strcpy(char* det, const char* src)
{
	assert(det && src);//与操作,一假全假,用来判断存不存在空指针
	char* s1 = det;//函数的返回值是原空间的起始地址,所以保存一下
	while (*det++ = *src++)//赋值语句的值是此时被赋值变量的值
	{
		;
	}
	return s1;
}

这里有很巧妙的一步,while (*det++ = *src++),一条语句,多个作用,我们都知道赋值语句,这个语句做判断条件时,它的值是被赋值变量的值,例如int a = 3; int b = 4; a = b;此时a = b语句是有值的,而且值就是 3 ,那么,在这里他的值就是 *det的值,这样,这个语句既起到了循环功能,赋值功能,还保证了一件事 ,那就是 src中的’\0’可以拷贝进去,因为赋值语句执行完毕后,while语句才进行判断,此时判断表达式值为0,也就说明已经拷贝完毕了,就可以退出循环了。

对于strcpy,我们还是要注意’\0’,要复制的字符串一定要有’\0’,目标空间一定要充足。

????strcat

MSDN:
在这里插入图片描述
先看描述,追加一个字符串,再看返回值,是char*,应该是返回原空间的首地址,再看两个参数
一个char *,一个const char*,那就是在char * 的结尾追加const类型的字符串喽,我们可以试一试。
在这里插入图片描述
虽然使用成功了,但是其中还有很多隐藏起来的问题,例如,如果追加的字符串没有’\0’,对被追加字符串的空间有没有要求,要是自己给自己追加呢?如果被追加函数在追加后仍剩余空间,那剩余空间的值会是什么?显然,这些问题就不止停留在使用的层面,我们开始探究他的原理,strcat是如何实现的?
1.既然我是一个字符串的追加函数,那我判断追加字符串到那里停止,当然是利用’\0’,如果没有’\0’,我就不知道要追加进去几个字符,可能会追加进去一些很奇怪的东西。
在这里插入图片描述
就像这里b字符串只有bbb,没有’\0’,这就导致了程序的错误。
2.被追加字符串的空间需要够大,这点很好理解,因为不够大,我追加进去的字符就越界了,会产生内存访问的错误。
在这里插入图片描述
3.一个看起来没必要,但是有可能犯的错误,被追加字符串必须是可修改的,不能是常量字符串。
4.如果被追加函数在追加后仍剩余空间,那他剩余空间的值会被赋成’\0’,所有剩余空间都是。
在这里插入图片描述
可以看到 6 7 8 号元素都被赋为’\0’了,而且从监视的窗口我们可以看到,追加是在被追加字符结尾的’\0’处就开始追加,然后还会把自带的’\0’,追加过来。
5.自己追加自己,会如何?首先知道这是一个错误的使用情况,具体为什么错误呢,需要深入了解函数的运行原理才能知道。
模拟实现:

函数的返回类型是 char*,参数是char *const char*

在这里插入图片描述
strcat函数的原理很简单,补充一下循环的终止条件,那就是arr2的’\0’处。同时细心的看官可以发现,strcat函数与strcpy函数有相似之处,只不过strcat函数是从目标函数的’\0’处开始。

char* my_strcat(char* det, const char* src)
{
	assert(det && src);
	char* s1 = det;
	while (*det)//找到被追加字符串'\0'处
	{
		det++;
	}
	while (*det++ = *src++)
	{
		;
	}
	return s1;
}

可以看到,strcat的实现就比strcpy多了一步查找被追加字符’\0’的循环,在这里解释一下之前说的自己追加自己的问题,如果是按照我们这个方法实现,他会出现一种情况,无限追加。
在这里插入图片描述
可以看到,自己追加自己,直接把’/0’替掉了,由于追加函数和被追加函数都是自己,所以字符串的’\0’直接消失了,没有终止条件,就会一直持续循环,直到越界。
附上strcat函数的源文件

char * __cdecl strcat (
        char * dst,
        const char * src
        )
{
        char * cp = dst;

        while( *cp )
                cp++;                   /* find end of dst */

        while((*cp++ = *src++) != '\0') ;       /* Copy src to end of dst */

        return( dst );                  /* return dst */

}

??strcmp

strcmp函数是我们常用的字符串比较函数,MSDN:
在这里插入图片描述

在这里插入图片描述

这个函数很常见,就简单介绍一下,给两个字符串,然后比较大小,既然是比较大小,那么第一个字符串大于第二个字符串就返回正数,等于返回0,小于返回负数。
至于他是怎么比较的,这里解释一下,strcmp函数比较的不是字符串的长度,而是字符串的大小, 例如: char a [] = "abcd"; char b[] = "abcdefg" ;char c = "abcf";,如果使用strcmp对这三个字符串进行比较,那会表明一种关系 a<b<c
在这里插入图片描述
这是为什么呢?原因是strcmp函数是一位一位的进行比较的,而且比较的是对应字符的ascll值,直到遇到不同的字符,或者两个字符同时遇见’\0’,就停止了,这三个字符串前三个abc都是相同的,直到第四个字符 ,’ d ‘<’ f ',所以b < c,a < c, 又因为 ’ \0 ‘<’ e '所以a < b。
模拟实现:
原理很简单,就是一个元素一个元素的比较,找到不同的,或者都遇到了’0’停止,函数返回值是一个int,两个参数都是const char *,

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);//先确定s1和s2不是空指针

	while (*s1 == *s2 && *s1)//如果相等就后移,如果相等且都等于'\0'直接返回0
	{
		s1++;
		s2++;
	}
	int ret = *s1 - *s2;
	return ((-ret < 0)) - (ret < 0);//后面有解释
}

另附strcmp源代码

int __cdecl strcmp (
        const char * src,
        const char * dst
        )
{
        int ret = 0 ;

        while((ret = *(unsigned char *)src - *(unsigned char *)dst) == 0 && *dst)
                {
                ++src, ++dst;
                }

        return ((-ret) < 0) - (ret < 0); // (if positive) - (if negative) generates branchless code
}

这个 return ((-ret) < 0) - (ret < 0);稍微解释一下,第一个括号里面如果ret是正值,他为真,第二个括号为假,1 - 0 = 1,大于返回的就是1,如果是负值ret 第一个括号为假,第二个括号为真,假-真 0-1= -1,如果两个都为0,返回值自然就是0。这里第一个括号为什么不用ret > 0这个我暂时也没找到原因,有知道的兄弟可以评论回复一下。

3.长度受限制的字符串函数

长度受限制的字符串函数有 strncpy,strncat, strncmp,原理和之前的函数大体相同,只是有了一个长度的限制,这里我们着重对比和演示并会附上源代码。

??strncpy

遇到一个新函数,MSDN:
在这里插入图片描述

可以看到,与strcpy相比,strncpy多了一个参数,参数类型是size_t,这个参数有什么作用呢?其实就是拷贝多少个字符串进去,你给它1,他就拷贝一个…这时要注意一件事,如果你是想覆盖目标空间的话,你输入的数字一定要比字符串的长度多一个,这一个,当然就是留给’\0’的。
在这里插入图片描述
假如不留空间:
在这里插入图片描述

就只是把前四个字符替换了,其他的没有变,因为没有把’\0’复制进去。
就算是大体相似的函数,也有值得我们注意的地方,如果我输入的数字大于拷贝字符串的长度,会怎么样?剩下的字符谁提供?如果大于目标空间的大小呢?
面对这些问题,我们调试走起:
在这里插入图片描述
可以看到,如果我们输入7,c是没有那么多字符的,那缺的字符找谁补呢?就找’\0’进行替补,那如果超出目标空间呢,毫无疑问,程序就崩溃了,越界访问了。
附源代码

char * __cdecl strncpy (
        char * dest,
        const char * source,
        size_t count
        )
{
        char *start = dest;

        while (count && (*dest++ = *source++) != '\0')    /* copy string */
                count--;

        if (count)                              /* pad out with zeroes */
                while (--count)
                        *dest++ = '\0';

        return(start);
}

可以说和之前的代码很相似,就是多了一个参数做循环的一个条件。

??strncat

MSDN:
在这里插入图片描述

又是多了一个参数 ,参数类型 size_t,限制位数的追加,整数是多少,就追加多少个字符进去,这里演示一下:
在这里插入图片描述
可以看到,我们输入的数字是2,他就只把两个字符追加了过去,由此我们还可以知道一件事,strncat函数自动在追加字符串的后面添加了一个’\0’,因为ab之后原本是没有’\0’的,而且strncat函数是从被追加函数’\0’处开始追加的,所以是函数给我们添加了一个’\0’进去。
在这里插入图片描述
还有一个问题,如果我们传入的数字大于追加字符串的长度,会发生什么呢?
在这里插入图片描述
可以看到,和原先没有什么差别,这是因为追加函数判断无论你输入再大的数,他到’\0’就会停止了,最多也就是把abcd录进去而已,这也是和strncpy函数的一点小小区别。
还有一件事,记不记得在说strcat函数的时候,我们自己追加自己,会出现无限追加的错误,那在strncat中会不会出现呢?不会出现了,这是因为覆盖不覆盖’\0’对我来说已经不重要了我到位数就会停止,而且我还会自己加上一个’\0’
在这里插入图片描述
附strncat的源代码:

char * __cdecl strncat (
        char * front,
        const char * back,
        size_t count
        )
{
        char *start = front;

        while (*front++)
                ;
        front--;

        while (count--)
                if ((*front++ = *back++) == 0)
                        return(start);

        *front = '\0';
        return(start);
}

注意 *front = '\0';这一步,就是在字符串的结尾追加一个’\0’,非常重要。

????strncmp

话不多说,MSDN:
在这里插入图片描述

也是多了一个参数,那这个函数的意思就是对比受限制位数的字符大小,比如我输入1,就前1个元素,输入2,就前两个元素,演示一下:
在这里插入图片描述
看,我输入数字3时,他是相等的,因为字符的前三个都是abc,那如果变成4呢?
在这里插入图片描述
这时就出现了差异,他们是不同的,‘d’<‘f’,所以值是-1。其他的原理和之前的strcmp函数差不多,这里不再赘述。
附strncmp源代码:

int __cdecl strncmp
(
    const char *first,
    const char *last,
    size_t      count
)
{
    size_t x = 0;

    if (!count)
    {
        return 0;
    }

    /*
     * This explicit guard needed to deal correctly with boundary
     * cases: strings shorter than 4 bytes and strings longer than
     * UINT_MAX-4 bytes .
     */
    if( count >= 4 )
    {
        /* unroll by four */
        for (; x < count-4; x+=4)
        {
            first+=4;
            last +=4;

            if (*(first-4) == 0 || *(first-4) != *(last-4))
            {
                return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4));
            }

            if (*(first-3) == 0 || *(first-3) != *(last-3))
            {
                return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3));
            }

            if (*(first-2) == 0 || *(first-2) != *(last-2))
            {
                return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2));
            }

            if (*(first-1) == 0 || *(first-1) != *(last-1))
            {
                return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1));
            }
        }
    }

    /* residual loop */
    for (; x < count; x++)
    {
        if (*first == 0 || *first != *last)
        {
            return(*(unsigned char *)first - *(unsigned char *)last);
        }
        first+=1;
        last+=1;
    }

    return 0;
}

📍小结

长度受限制的字符串函数和不受限制的字符串函数已经介绍完毕,相信大家对字符串也有了一定的了解,希望大家在看博客之余,能够多多实践,动手练习,也希望能指点指点博主,在下一篇博客中,将会介绍其他三类函数。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:46:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 4:04:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码