在 C 语言我们学习了字符串的一系列操作,比如拷贝,比较等一系列的操作,那么是否有更加全面的函数呢,现在我们来学习直接通过内存访问来操作一些数据的函数
首先我提出一些学习方法,在 C 语言库中有许许多多的函数,而很多同学觉得有些函数过两天就忘了,分享一下我在学习各种很重要的函数时一些实用性很强的学习建议
1.关注函数的类型,返回类型,参数类型,参数名,参数个数、
2.关注函数的作用,通过作用来更好的理解函数的各种参数和返回类型,例如 strstr 函数,这个函数是寻找子串,所以参数肯定至少有两,一个待查找串,一个模板串,而且是查找,所以找到了会返回一个地址,那这个函数的返回类型就是 char*
3.手动的去实现一些函数,例如上节博客的字符串函数,以及这节的内存操作函数,手动去实现,不仅仅可以增加我们对函数的认识,还可以从中获取一些,操作的算法,甚至能学习到指针的实现过程,这是极大的学习函数和算法,逻辑的宝贵途径
1.Memcpy
内存拷贝函数,这个函数是处理内存不重叠时所使用的,如有内存重叠,在同一块内存上,我们应该使用 memmove 函数,下面我给出这个函数的类型
void * memcpy(void *dest,void *src,size_t num)
返回类型是泛型指针void*,也代表了这个函数可以进行各种数据的操作,返回拷贝内容的起始地址dest,后面的参数 size_t num 指的是拷贝的字节大小,整体的功能是将 src里的内容拷贝到 dest 里并返回 dest 的地址
下面,我给出这个函数的模拟实现
void * my_memcpy(void * dest,void * src,size_t num)
{
void * ret = dest;//保留起始位置,方便返回
assert(dest && src);
while(num--)//循环num 次,按字节交换
{
*(char *)dest = *(char *)src;
dest = (char *)dest +1;
src = (char*)src + 1;
}
return ret;
}
2.Memmove
这个函数与上一个函数的功能更加强大,可以处理带有内存重叠的拷贝
void * memmove(void *dest,void *src,size_t num)
比如,当有一个数组,需要在数组内部实现拷贝且带有内存的重叠时,就可以使用这个函数
给出这个函数的模拟实现
void * my_memmove(void * dest,void*src,size_t num)
{
void * ret = dest;
assert(dest && src);
if((char *)dest < (char *)src)
{
while(num--)//从前向后拷贝
{
*(char*)dest = *(char*)src;
dest = (char *)dest +1;
src = (char *)src +1;
}
}
else
{
while(num--)//从后向前拷贝
{
*((char *)dest+num) = *((char *)src +num);
}
}
return ret;
}
2.实现原理
注意,这个函数内部的实现思想是重要的,在后续实现顺序表时,就会用到这种,怎样拷贝才不会让值覆盖,所以,对这个函数的内部实现清楚,是让自己能力提升的重要因素
我来讲解一下这个函数
在 memcpy 中,我们没有关心 dest 与 src 的位置前后问题,试想,如果 dest 在 src 后面,我们在从src 的开始地址复制时,值就会被覆盖,比如 dest 在[4],src在[2],我们要向后拷贝两个单位,
比如从 下标为 2 开始往后复制三个单位到下标为 3 的数据,2->3,3->4,注意,当 3 拷贝到 4 时,3 已经被 2 的值覆盖了,所以 4 也是 2 的值,此时就发现了问题,我们的模拟实现是有问题的,所以我们要分情况,在这种情况下,我们应该从后往前开始拷贝,在代码中就体现在以下地方
按 dest 和 src 的位置前后分类,当 dest 在 src 前面时,我们应该从前往后复制,当 dest 在 src 后面时,我们应该从后往前复制,我对此总结为:
前前后后,即当dest 在 src 前面时,从前往后复制,当 dest 在 src 后面时,从后往前复制
3.Memset
这个函数的功能类似于初始化一组数据,但在运用时有局限,以下是函数定义
void * memset ( void * ptr, int value, size_t num );
有三个参数,第一个指针指向待开始的设置的数据,第二个参数是改变后的元素,第三个是设置多少个字节。下面给出模拟实现
void *my_memset(void *s, int c, size_t n) {
if (s == NULL) return s;
unsigned char *p = (unsigned char *)s;
unsigned char uc = (unsigned char)c;
// 指针遍历
while (n--) {
*p++ = uc;
}
return s;
}
但注意,此函数在使用时是有局限的,例如在此时
当设置的元素是整形时,一个整形是四个字节,而 memset 是按字节去填充内容,那么在一个整形内就会填充四次,填充完之后,那这个整形早已不是我们所期望的目标整形,当然也并不是所有整形都不行,当设置元素为 0 时,就可以解决这个问题。
好了,到现在我们重要的内存操作讲解就已经完毕了,在我讲解时,都采取了我一开始总结的学习函数的方法,关注函数的返回类型和各个参数,关注函数的作用,关注函数的模拟实现,相信你如果以后按照这种思想学习函数时,会对函数的使用了如指掌。
以上纯个人观点,如有不足,大家补充
1116





