内存函数
string库函数不仅提供了如strcmp、strcat、strcmp等函数,还存在memcpy、memmove、memset、memcmp等功能相似但实现方式主要通过使用指针的内存函数,但这种内存函数不仅可以对char类型进行处理,还可以在其它类型上使用。今天就来大致谈谈这几个内存函数的作用,使用方法与部分的实现过程。
memcpy
memcpy与strcpy的作用相类似,都可以将一个变量的内容拷贝到另一个变量之中,不过memcpy函数是针对内存块进行拷贝,函数的定义如下:
destination为拷贝到的内存的开始地址,source为拷贝内容来源的内存的开始地址,而num为拷贝的内存大小,单位为字节,也就是说num为需拷贝的元素个数与变量的类型大小的乘积,函数的返回值为void*。该函数
的实现主要通过将内存以字节为单位进行逐个拷贝,主要代码如下:
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
通过将destination与source进行强制类型转换位char*类型,便可以单个字节为单位进行数据拷贝。但是这种方法在二者内存地址重叠的情况下,复制的结果都是未定义的。即如果destination与source指向同一个数组,destination在source之后,且source+num大于destination的情况下,之前拷贝的值会将source覆盖,使拷贝的结果与预期不相符。但此时我们便可以考虑使用memmove函数。
memmove
memmove函数的功能与使用方式与memcpy函数基本相同,但是该函数却能避免memcpy函数在二者内存地址重叠的情况,此函数的定义如下:
定义基本与memcpy函数相同,而主要代码如下:
if (destination < source)
{
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
else
while (num--)
*((char*)destination + num) = *((char*)source + num);
可见,通过判断destination与source的位置,从而决定拷贝的顺序,当destination在source之后时,从后往前进行拷贝便可以规避memcpy函数出现的问题。
memset
memset函数的功能主要是以字节为单位将内存中的值设置为自己想要的内容。该函数的定义如下:
其中ptr是希望设置的内存的起始地址,value为想要设置为的内容,num则为想要设置的字节长度。示例如下:
char str[] = "hello world!";
memset(str, '#', 6);
通过使用memset便可以将str字符串的前6个字符设置为#,str变更为######world!.
memcmp
memcmp函数则是一个与strcmp函数功能相类似的内存函数,但是以字节为单位比较内存的大小,函数定义如下:
即从ptr1和ptr2指针指向的位置开始,比较二者向后num个字节的的大小。
数据在内存中的存储方式
整数在内存中的存储
整数的二进制的表示方法共有三种:原码、反码和补码。
对于有符号的整数,其三种表示方式都存在符号位与数值位,最高一位为符号位,其余位则都为数值位。符号位为1表示负数,为0则表示正数。
对于正整数,其原码、反码和补码都相同;对与负整数,其反码是将原码除符号位都按位取反,补码则是将反码加1。
而在计算机系统中,数值⼀律⽤补码来表⽰和存储。其原因在于,使⽤补码,可以将符号位和数值域统⼀处理;同时,加法和减法也可以统⼀处理(CPU只有加法器),此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
接下来,让我们看一段整数与他在内存中的存储方式:
int a = 0x11223344;
图1是a整数的16进制表示,而图2则是在VS2022的调试界面显示的a在内存中的存储形式,我们可以发现整数a在内存中是倒着存储的,这是为何呢?这便牵扯到了我们接下来要讲的大小端字节序的问题。
大小端字节序
当我们存储大于一个字节的数据时,例如在C语言中除了1个字节大小的char类型,还存在2个字节的short类型,4个字节的int类型等等,而且在使用大于8位的处理器,如16位或32位处理器时,就必然存在如何安排多个字节数据的问题,在存储这些数据时便需要考虑数据在内存中的存储顺序的问题,而根据不同的存储顺序,我们便分成了大端字节序存储与小端字节序存储两种方式。
大端字节序存储:指将数据的低位,放在内存的高地址处,而将数据的高位存储到内存的低地址处。按照先前的例子,44便是该数据的地位,而将44存储到高地址处,将数据的高位11放到地址这便是大端字节序存储。
小端字节序存储:指将数据的高位,放在内存的高地址处,而将数据的低位存储到内存的低地址处。由先前的例子表明该电脑的数据时通过小端字节序存储值内存的。
同时我们可以同过以下代码却认设备为大端存储还是小端存储:
#include <stdio.h>
int check_system()
{
int n = 1;
return *(char*)&n;
}
int main()
{
int ret = check_system();
if (ret == 1)
printf("系统为小端!\n");
else
printf("系统为大端!\n");
return 0;
}