C语言内存操作函数使用示例梳理讲解

  目录

  一、memcpy()

  函数原型

  void * memcpy ( void * dest, const void * src, size_t num );

  参数说明

  模拟算法

  void* my_memcpy(void* dest, const void* src, size_t num) {

  void *ret = dest; //保存dest,用于最终输出

  assert(dest);

  assert(src);

  while (num--) {

  *(char*)dest = *(char*)src1; //(char*): 取出每一个字节(8 bit)的值,以单个字节为单位进行赋值

  dest = (char*)dest + 1;

  src = (char*)src + 1;

  } //void * 类型是不能直接运算的,因为没有步长。(char*)将dest与src转换为以char为步长,再向后移动

  return ret;

  }

  根据memcpy()的模拟算法,如果src与dest的内存空间有重叠部分,则可能导致src中的内容被覆盖,无法输出正确的值。

  src中元素3的位置恰好也是dest中首元素的位置。dest的首元素被更改的同时,src中的元素也被更改。因此,memcpy()是不可用于“自己拷贝到自己后面”这样的操作的。这一问题留给了memmove()来解决。

  使用示例

  1.简单

  int main()

  {

  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

  int arr2[10] = { 0 };

  memcpy(arr2, arr1, 20); //拷贝20个字节,即5个int元素

  float arr3[] = { 1.0f,2.0f,3.0f,4.0f };

  float arr4[5] = { 0.0 };

  memcpy(arr3, arr4, 8); //拷贝8个字节,即2个float元素

  return 0;

  }

  2.进阶

  //示例来自cplusplus官网

  /* memcpy example */

  #include

  #include

  struct {

  char name[40];

  int age;

  } person, person_copy;

  int main ()

  {

  char myname[] = "Pierre de Fermat"; //定义一个字符串

  /* 用 memcpy 拷贝字符串 */

  //每个char类型占一个字节,因此要拷贝的字节数即strlen()+1,加一是因为要把'0'也拷贝过去。

  memcpy ( person.name, myname, strlen(myname)+1 );

  person.age = 46;

  /* 用 memcpy 拷贝结构体 */

  //sizeof操作符,可以直接得到结构体变量在内存中所占的字节数。

  memcpy ( &person_copy, &person, sizeof(person) );

  //直接完成了结构体之间的数据拷贝:从person拷贝到person_cpy,不用手动转义,非常方便

  printf ("person_copy: %s, %d

  ", person_copy.name, person_copy.age );

  return 0;

  }

  二、memmove()

  函数原型

  void * memmove ( void * dest, const void * src, size_t num );

  参数说明

  模拟算法

  #include

  #include

  //模拟实现memmove()

  void* my_memmove(void* dest, const void* src, size_t num)

  {

  void* ret = dest; //保存结果用于输出

  //从前向后拷贝,也可以写成if(dest <= src || (char*)dest >= (char*)src + num)

  if (dest <= src)

  {

  while (num--) {

  *(char*)dest = *(char*)src; //拷贝

  dest = (char*)dest + 1;

  src = (char*)src + 1; //指针从前向后移动(从低地址向高地址移动)

  }

  }

  else //从前向后拷贝

  {

  dest = (char*)dest + num - 1;

  src = (char*)src + num - 1; //初始化两指针至各自范围的最后

  while (num--) {

  *(char*)dest = *(char*)src; //拷贝

  dest = (char*)dest - 1;

  src = (char*)src - 1; //指针从后向前移动

  }

  }

  return ret; //返回值为dest

  }

  //测试代码///

  int main() {

  int arr1[] = { 2,3,4,5,6 };

  my_memmove(arr1+2, arr1, 8);

  //预计 2 3 2 3 6

  for (int i = 0; i < 5; i++) {

  printf("%d ", arr1[i]);

  }

  printf("

  -------------------

  ");

  int arr2[] = { 2,3,4,5,6 };

  memmove(arr2 + 2, arr2, 8);

  for (int i = 0; i < 5; i++) {

  printf("%d ", arr2[i]);

  }

  return 0;

  }

  拷贝顺序的结论如图所示。上面提到,当有内存重叠时,拷贝的顺序是有讲究的。若不遵守下图的结论, 仍将导致src中原来的值被覆盖,无法输出正确的结果。

  ***错误的模拟算法

  如下代码是错误的:

  //错误的代码

  void* my_memmove(void* dest, const void* src, size_t num)

  {

  void * ret = dest;

  while (num--) {

  //从前向后拷贝

  if (dest <= src)

  {

  *(char*)dest = *(char*)src;

  dest = (char*)dest + 1;

  src = (char*)src + 1;

  }

  else

  {

  dest = (char*)dest + num - 1; //错误

  src = (char*)src + num - 1; //错误

  *(char*)dest = *(char*)src;

  dest = (char*)dest - 1;

  src = (char*)src - 1;

  }

  return ret;

  }

  有一些同学可能认为,while(num--)语句与if语句的顺序可以调换。正确的代码是先进行情况判断,再进入while(num--)进行赋值与移动。将二者顺序调换,乍一看没有什么问题,是先设定一共要移动num次,再进入判断进行具体的操作。但是这样书写是有问题的,因为在dest > src && dest < src+num时,需要从后向前拷贝,这意味着dest和src的起始位置要发生变化。

  上述代码中标记“错误”的语句便是dest和src初始化的语句。如果直接将while和if的位置调换,则每一次进入循环,都要初始化一遍dest和src。如此,dest与src的功能就被打乱了。

  使用示例

  //示例来自cplusplus官网

  /* memmove example */

  #include

  #include

  int main ()

  {

  char str[] = "memmove can be very useful......";

  memmove (str+20,str+15,11); //表示将包括str+15向后11个字节的内容移动到str+20位置

  puts (str);

  return 0;

  }

  输出:

  如下图,将 src = str+11 位置开始,包括该位置共向后拷贝11字节。每个char占一个字节,因此拷贝了"very useful"这7个char字母至dest = str+20的位置。

  三、memset()

  函数原型

  void * memset ( void * ptr, int value, size_t num );

  参数说明

  使用说明

  例如,有数组arr:[ 1 2 3 4 5 ]

  要将其前两个元素值设定为0,则可使用memset函数:memset(arr, 0 , 8)

  输出:[ 0 0 3 4 5 ].原理如下(以小端存储的形式展现):

  arr数组为int类型,一个int为4字节、32bit,内存中的存储如下(二进制):

  01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  memset(arr, 0, 8),将前8个字节(2个int)置为0(其实每个比特位都被置为0了,但由于其它的比特位已经为0,故没有标出来)

  但若使用 memset(arr, 1, 8),并不是把数组前两个元素置为 1 .因为memset()函数是针对内存中每个字节的,memset(arr, 1, 8)的实际作用是将前8个字节中的内容全部置为1.

  00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01

  00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01

  03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  此时,前4字节按照int类型解析出来,结果为1000000010000000100000001,即16843009。

  总结:

  使用示例

  //示例来自cplusplus官网

  /* memset example */

  #include

  #include

  int main ()

  {

  char str[] = "almost every programmer should know memset!";

  memset (str,'-',6); //表示将从str开始,包括str向后6个字节的内存内容置为'-'

  puts (str);

  return 0;

  }

  //输出:------ every programmer should know memset!

  四、memcmp()

  函数原型

  int memcmp ( const void * ptr1, const void * ptr2, size_t num );

  参数说明

  使用说明

  返回值为整型,若返回值>0,则ptr1的内存长度大于ptr2;若返回值==0,则二者相等;若返回值<0,则ptr1的内存长度小于ptr2

  使用示例

  //示例来自cplusplus官网

  /* memcmp example */

  #include

  #include

  int main ()

  {

  //创建两个要用作比较的数组

  char buffer1[] = "DWgaOtP12df0";

  char buffer2[] = "DWGAOTP12DF0";

  //接受比较的结果

  int n;

  //要比较的字节数为buffer1的长度

  //两字符串的比较可以用strcmp(buffer1,buffer2)函数实现,原理大致相同。

  n = memcmp ( buffer1, buffer2, sizeof(buffer1) );

  if (n>0) printf ("'%s' is greater than '%s'.

  ",buffer1,buffer2);

  else if (n<0) printf ("'%s' is less than '%s'.

  ",buffer1,buffer2);

  else printf ("'%s' is the same as '%s'.

  ",buffer1,buffer2);

  return 0;

  }

  到此这篇关于C语言内存操作函数使用示例梳理讲解的文章就介绍到这了,更多相关C语言内存操作函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

  您可能感兴趣的文章: