【C语言】strncpy,strncat,strncmp,memcpy,memmove,memcmp,memset的模拟实现


首先注意,接下来的函数基本都需要目标内存块足够大。
如果不够大很有可能会产生下述情况:

  1. 比较结果不准确:如果目标函数不够大,比较的字符数量较少,可能无法完整比较两个字符串的内容。这样就无法得到正确的比较结果,可能会导致程序逻辑错误。
  2. 隐患安全问题:如果目标函数不够大,比较的字符数量小于其中一个字符串的长度,可能会导致缓冲区溢出的安全问题。因为在比较过程中,如果需要比较的字符串长度超过了目标函数的大小,就会访问到未分配的内存空间,可能导致程序崩溃或者被攻击者利用。
  3. 性能影响:如果目标函数不够大,比较的字符数量较少,可能需要多次调用strncmp函数才能完成完整的字符串比较。这样会增加函数调用的开销和执行时间。

strncpy(替换掉原有字符串的指定位置)

概念
在这里插入图片描述
上面这张图是MSDN上对strncpy所需要的形参类型以及返回类型的介绍,我们可以知道,这个函数的头文件为<string.h>,需要输入一个目标地址,一个源头地址,以及拷贝多少个字符,最后返回的是一个字符指针

(关于指针的一些详细介绍,本人主页有,不嫌弃可以看看下)

在这里插入图片描述

然后这张是这个函数的一个实现案例,它先把"Dogs"的4个字符全部拷贝到string里,再把"mean"的四个字符拷贝到string+9的位置,随后实现了string里字符串的变化

在这里插入图片描述
这里是cpluscplus对strncpy的注意声明,大概意思如下

  • 拷贝num个字符从源字符串到目标空间。
  • 如果在复制num个字符之前找到源C字符串的结尾(由null字符表示),则目的地将用零填充,直到向其写入总数为num个字符。
  • 如果source长度大于num,则不会在destination的末尾隐式添加空字符。因此,在这种情况下, destination不应被视为以空结尾的C字符串(这样读取会溢出)。
  • destination和source不应重叠(重叠时,请参阅memmove以获得更安全的替代方法)。

实现
这个函数的实现较简单,只需要找对destination和source的位置,然后用佛如循环一个一个改变,唯一要注意的是,因为再循环后往往destination的地址惠王后偏移num个长度,所以需要提前备份一个destination的地址

代码如下

char* my_strncpy(char* dest, const char* src, int numsSize)
{
	char* cp = dest;
	while (numsSize--)
	{
		*dest++ = *src++;
	}
	return cp;
}

strncat(追加指定长度的字符串到原有的字符串后面)

概念
在这里插入图片描述
上面这张图是MSDN上对strncat所需要的形参类型以及返回类型的介绍,我们可以知道,这个函数的头文件为<string.h>,需要输入一个目标地址,一个源头地址,以及拷贝多少个字符,最后返回的是一个字符指针,基本和strncpy一样

在这里插入图片描述

  • 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字
  • 如果source指向的字符串的长度小于num的时候,只会将字符串中到\0 的内容追加到destination指向的字符串末尾

在这里插入图片描述
这是MSDN上的案例,不过多赘述。
实现

这里除了跟strncpy一样需要提前备份一次,还需要通过循环*dest++达到destination字符串最后一位的效果,并且如果source指向的字符串的长度小于num的时候,只会将字符串中到\0 的内容追加到destination指向的字符串末尾,需要在返回前加上一个’\0’

代码如下

char* my_strncat(char* dest, char* src, size_t num)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest++)//循环直到dest指向'\0'
		;
	dest--;//消除遇到'\0'时的++
	while (num--)
	{
		if ((*dest++ = *src++) == 0)//当src结束但num还没结束时,就直接返回
			return ret;
	}
	*dest = '\0';
	return ret;
}

strncmp(比较两字符串的前指定长度)

概念
在这里插入图片描述
上面这张图是MSDN上对strncmp所需要的形参类型以及返回类型的介绍,我们可以知道,这个函数的头文件为<string.h>,需要输入两个比较字符串的地址,以及比较多少多少个字符,最后返回的是一个整形

在这里插入图片描述

然后是cplusplus上的介绍,大概意思就是,将C字符串stri的最多num个字符与C字符串str2的字符进行比较。这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续执行后面的对,直到字符不相同,直到达到终止的空字符,或者直到两个字符串中的num字符匹配,以先发生的为准。

在这里插入图片描述

⽐较str1和str2的前num个字符,如果相等就继续往后⽐较,最多⽐较num个字⺟,如果提前发现不⼀
样,就提前结束,⼤的字符所在的字符串⼤于另外⼀个。如果num个字符都相等,就是相等返回0.

在这里插入图片描述
这是MSDN上的例子,也是比较简单的,有兴趣看看,就不多说了。

实现
这里我们子需要一直循环,知道两个字符串布希昂同,或者其中一个到最后了,最后返回两者的相减,就可以根据返回值与0关系,知晓判断结果

代码如下

int my_strncmp(const char* str1, const char* str2, size_t nums)
{
	assert(str1 && str2);
	while (nums-- && *str1 == *str2)
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

注意
上面的str开头的几个函数,只能作用于字符类型的对象,所以如果遇到非字符类型的对象就需要用到mem开头的函数

memcpy(替换掉目标内存的前指定长度元素)

概念
在这里插入图片描述
我们可以发现,这里memcoy的返回值时无类型指针,目标地址和源头地址也是无类型指针,也需要一个决定拷贝多少个字节的count,头文件可以是string.h也可以是memory.h

这里我们可以注意下,之所以用void* 类型的目的就是能够让所有类型的对象都适用,而count的单位的不是对象类型的单位而是字节

在这里插入图片描述

翻译如下

  • 将num字节的值从源指向的位置直接复制到目标指向的内存块。
  • 源指针和目标指针所指向的对象的底层类型与此函数无关; 结果是数据的二进制副本。
  • 该函数不检查源中是否有任何终止null字符——它总是精确地复制num个字节。
  • 为了避免溢出,目标参数和源参数所指向的数组的大小应该至少为num字节,并且不应该重叠(对于重叠的内存块,emmove是一种更安全的方法)。

在这里插入图片描述
案例如上,摆烂了…

代码实现基本与strcpy类似,唯一需要注意的时,这是不论是形参还是返回的都是无类型指针,用来适应所用的类型,但是在函数累不,为防止各种情况的出现,我们选用char* 类型,以字节为最小单位,保证只要不是出现比字节还小的单位就能够做到交换。
值得一提的是,8,9行为推进循环而要往下看一个字节的地址,不能够直接加上1,而需要先转换为char* 类型,确保每次加一的距离都是一个字节,而重复char* 的原因是这种强制类型转换只是临时的,所以每次需要使用时,都必须强制类型转换。

代码如下

void* my_memcpy(void* dest, void* src, size_t n)
{
	char* ret = dest;
	assert(dest && src);
	while (n--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return (ret);
}


memmove(复制源内存块的指定长度到目标内存块)

概念

在这里插入图片描述
这里memmove的返回值时无类型指针,目标地址和源头地址也是无类型指针,也需要一个决定拷贝多少个字节的count,头文件是string.h
这个函数和memcpy的差别就在于,memcpy只能用于不重叠的源内存块和⽬标内存块,但是memmove可以。

在这里插入图片描述
翻译如下

  • 将num字节的值从源指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样,允许目标和源重叠。
  • 源指针和目标指针所指向的对象的底层类型与此函数无关;结果是数据的二进制副本。
  • 该函数不检查源中是否有任何终止null字符–它总是精确地复制num个字节。
  • 为了避免溢出,目的参数和源参数所指向的数组的大小至少为num字节

在这里插入图片描述
案例如上

实现
这里我们需要注意的是,关注这个函数的源内存块和⽬标内存块的相对位置,我们可以简单看为三种情况
dest与src重叠,且在src后面,在src前面,以及与src毫不相关。大致图示如下
在这里插入图片描述
这样以来的话,我们之选哟分三种情况

  • dest与src重叠,且在src后面:从后往前赋值
  • dest与src重叠,且在src前面:从前往后赋值
  • dest与src不重叠:随意

为了使代码尽可能地简单,我们就分为
dest在src前面和dest在src后面这两种即可

代码如下

void* my_memmove(void* dest, void* src, size_t n)
{
	char* ret = dest;
	assert(dest && src);
	if (src < dest)
	{
		dest = (char*)dest + n;
		src = (char*)src + n;
		while (n--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest - 1;
			src = (char*)src - 1;
		}
	}
	else
	{
		while (n--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

memcmp(比较任意类型内存块的前指定长度元素)

概念

在这里插入图片描述
这里memcmp的返回值时无类型指针,两个比较量也是无类型指针,以及一个决定比较多少个字节的count,头文件可以是string.h也可以是memory.h

在这里插入图片描述

  • 将ptri所指向的内存块的前num字节与ptr2所指向的前num字节进行比较,返回如果全部匹配则为零,如果不匹配则为不同于零的值。
  • 注意,与strcmp不同,该函数在找到空字符后不会停止比较。

返回值如下
在这里插入图片描述

在这里插入图片描述
案例如上

这里的代码实现基本就是char* 版的strcmp不过多赘述了,唯一不同的就是他遇到空字符不停止,也是一直比较到出现不同处的位置,最多比较num次


memset(设置目标内存的指定长度为想要的内容)

概念
在这里插入图片描述
这里memset返回的是无类型指针,形参是一个无类型目标内存块,需要设置的值。该值作为int类型传递,但函数使用unsignedchar填充内存块,以及记录需要改变多少字节的count

memset的目的是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。

在这里插入图片描述

案例如上

实现
这个代码是通过指向的空间前size个字节,初始化ASCII为c的值来实现的
同样也需要备份一次dest

void* my_memset(void* str, int c, size_t count)
{
	assert(str != NULL);
	char* cp = str;
	while (count--)
	{
		*(char*)str = (char)c;
		str = (char*)str + 1;
	}
	return cp;
}


strchr(判断一个字符在另一个字符串里是否存在)

概念

在这里插入图片描述
上图是MSDN对strchr的介绍,我们需要输入一个字符串和一个带查找字符,并且放上<string.h>的头文件

关于为什么c的输入类型为int类型,是因为要定位的字符。它作为其int提升传递,但为了进行比较,它在内部被转换回char。
在这里插入图片描述
这个函数最终会返回要定位字符首次出现在字符串中的地址
如果没有出现则会返回NULL

举例如下

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

char* strchr (
    const char* string,
    int ch
) {
    while (*string && *string != (char)ch)
        string++;

    if (*string == (char)ch)
        return ((char*)string);
    return (NULL);
}
  • 32
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值