• <string.h>字符操作函数的实现(strcpy、strcat、mem)


    在这里插入图片描述

    拷贝函数

    strcpy

    在这里插入图片描述
    实现:

    char* My_Strcpy(char* des, const char* src)
    {
    	assert(des);
    	assert(src);
    	char * ret = des;
    	/*do{
    		*ret = *src;
    		ret++;
    	} while (*src++);*/
    
    	//while (*src){
    	//	*ret = *src;
    	//	ret++;
    	//	src++;
    	//}
    	//*ret = '\0';
    
    	while (*ret++ = *src++);  // 为'\0'时退出循环
    	/*  因为后缀++优先级高于解引用。先进行后置++
    	(后置++,先使用值再自增,效率低于前置++,因为要开辟临时空间,++i可以做左值,i++不行)
    	然后解引用,对指针+1(加其所指向类型的大小)。*/
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    memcpy

    在这里插入图片描述

    void* My_Memcpy(char* des, const char* src, size_t num)     
    {
    	assert(des);
    	assert(src);
    	void* ret = des;
    	while (num--)
    	{
    	// 逐字节拷贝
    		*(char*)des++ = *(char*)src++;
    	}
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    memcpystrcpy的区别

    1. 复制的内容不同。
      strcpy只能复制字符串,而memcpy以字节为单位复制,可以复制任意内容,例如字符数组、整型、结构体、类等。memcpy通常与memset函数配合使用。
    2. 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符\0才结束,所以容易溢出。memcpy则是根据其第3个参数num决定复制的长度。

    memmove

    在这里插入图片描述

    memcpymemmove的区别
    它们唯一的区别是当内存发生局部重叠时,memmove可以保证拷贝正确,memcpy拷贝的结果是未定义的(取决于编译平台内部对memcpy的优化处理)。

    实现:

    void* My_memmove(void* dst, const void* src, size_t num)
    {
    	assert(dst);
    	assert(src);
    	char * _dst = (char *)dst;
    	char * _src = (char *)src;
    
    	if (_dst > _src && _dst < _src + num){   //内存重叠情况 
    	// 从高地址——>低地址拷贝
    		_dst = _dst + num - 1;
    		_src = _src + num - 1;
    		while (num--){
    			*_dst-- = *_src--;
    		}
    	}
    	else{                                       //memcpy
    		while (num--){
    			*_dst++ = *_src++;
    		}
    	}
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    strncpy

    在这里插入图片描述

    char* My_Strncpy(char* dest, const char* src, size_t num)
    {
    	assert(dst);
    	assert(src);
        char *tmp = dest;
        while (num) {
            if ((*tmp = *src) != 0)
                ++src;
            ++tmp;
            --count;
        }
        return dest;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    strcpystrncpy的缺陷

    1. 存在潜在越界问题
      当dest的长度 < src的长度的时候,由于无法根据指针判定其所指指针的长度,故数组内存边界不可知的。因此会导致内存越界,尤其是当数组是分配在栈空间的,其越界会进入你的程序代码区,将使你的程序出现非常隐晦的异常。
    2. 字符串结束标志服’\0’丢失
      当dest所指对象的数组长度==count的时候,调用strncpy使得dest字符结束符’\0’丢失。
    3. 效率较低
      当count > src所指对象的长度的时候,会继续填充’\0’知道count的长度为止。
    4. 不能处理内存覆盖问题
      不能处理dest和src内存重叠的情况。

    拼接函数

    strcat

    在这里插入图片描述

    char* My_Strcat(char * dst, const char * src)
    {
    	assert(dst!= NULL && src != NULL);  //保证dest、src非空
    	while (*dst){
    		dst++;
    	}
    	while (*src){
    		*dst++ = *source++;
    	}
    
    	return dst;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    strncat

    在这里插入图片描述

    
    char* My_strncat(char* dst, const char* src, size_t num)
    {
    	assert(dst!= NULL && src != NULL);  //保证dest、src非空
    	char* ret = dst;  
    	while (*dst!= '\0')  //用指针往后一个个找,找到dest结尾的‘\0’
    	    dst++;
    	while (num && *dst){
    		*dst++ = *src++;
    		--num;  
    	}     
    	*dst= '\0';     
    	return ret;   //返回dst字符串起始地址
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其他

    memset

    strlen

    在这里插入图片描述

     // 1.计数器方式
    size_t My_Strlen(const char *str )    
    {
    	size_t res = 0;
    	while (str[res]){
    		res ++;
    	}
    	return res ;
    }
    
     //2. 递归模拟(不创建临时变量)
     size_t My_Strlen(const char* str )    
    {
    	if (*str == '\0'){
    		return 0;
    	}
    	return 1 + My_Strlen(str + 1);
    }
    
    // 3.指针减指针(中间越过元素个数)
    size_t My_Strlen(const char *str)        
    {
    	char *p = str;
    	while (*p != '\0'){
    		p++;
    	}
    	return p - str;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    memset

    在这里插入图片描述

    void* My_Memset(void* des, int val, size_t num)
    {
    	assert(des);
    	char *p = (char *)des;
    	while (num){
    		--num;
    		*p++ = value;
    	}
    	return des;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    python创建分类器小结
    【rtp-benchmarks】对接发送侧,实现基于uvgRtp的多线程接收
    Tomcat HTTPS证书申请与部署
    自学Java很困难?那是你没找到方法
    Cookie和Session学习总结
    C++标准输入输出及命名空间
    [内存泄漏][PyTorch](create_graph=True)
    celery笔记四之在Django中使用celery
    23 Python的shutil模块
    SpringBoot+Vue项目校园在线拍卖系统
  • 原文地址:https://blog.csdn.net/weixin_45910068/article/details/126927746