这里写目录标题
首先注意,接下来的函数基本都需要目标内存块足够大。
如果不够大很有可能会产生下述情况:
- 比较结果不准确:如果目标函数不够大,比较的字符数量较少,可能无法完整比较两个字符串的内容。这样就无法得到正确的比较结果,可能会导致程序逻辑错误。
- 隐患安全问题:如果目标函数不够大,比较的字符数量小于其中一个字符串的长度,可能会导致缓冲区溢出的安全问题。因为在比较过程中,如果需要比较的字符串长度超过了目标函数的大小,就会访问到未分配的内存空间,可能导致程序崩溃或者被攻击者利用。
- 性能影响:如果目标函数不够大,比较的字符数量较少,可能需要多次调用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的地址
代码如下
c
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'
代码如下
c
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关系,知晓判断结果
代码如下
c
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* 的原因是这种强制类型转换只是临时的,所以每次需要使用时,都必须强制类型转换。
代码如下
c
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后面这两种即可
代码如下
c
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
c
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;
}