一、动态内存管理
通常我们在内存中开辟空间的方法,就是用定义一个固定大小的数组,这样虽然直接,但是如果存储的数据很多的时候就很容易造成空间不足,我们就需要一个动态的存储方式来存储数据。
有一些函数可以动态开辟内存:malloc,free
malloc:
void* malloc(size_t size);
malloc函数在执行时,如果开辟成功会直接向内存申请一段连续可用的空间,返回值是指向这个空间首地址的指针;如果开辟失败则会返回一个NULL指针,所以在开辟空间的时候一定要进行检查。
在申请空间的时候,系统从堆空间(小地址开始到大地址)处向上随机一个找符合的空间来完成开辟,如果找不到符合条件的空间,就会返回一个NULL来表示没有该空间,所以当储存数据很多的时候,一定要对malloc开辟的空间进行判断是否开辟成功。
calloc:
void* calloc(size_t num,size_t size);
calloc函数在开辟空间时,会开辟num个大小为size的连续空间,并把空间的每一个字节都初始化为0,该函数也存在开辟不成功的例子,所以也需要进行判断。
realloc:
void* realloc(void* ptr,size_t size);
realloc函数对动态内存的调整比较灵活,不仅可以做到对动态开辟内存大小的调整,还可以将原来内存中数据都移动到新开辟的空间,其中ptr是要调整的内存的首地址,size是调整后内存的新大小,其返回值是调整后内存的首地址(可能是原来的地址,也可能是一个新的地址)。既然有空间申请函数,那么一定有空间释放函数来释放掉那些没用的空间,做到空间利用率最大。
free:
void (void* ptr);
free函数可以释放掉动态开辟的内存。如果参数ptr是一个NULL指针,则函数什么都不做。在内存的释放的过程中,有这么一个原则,一个开辟多大的空间,释放的时候必须全部释放掉,不能只释放这个空间的一部分。
关于空间的释放,如果一段空间不使用了,不释放掉这个空间,就会造成空间的浪费,在数据占用非常大的时候,就显得空间不够用,试想这些没有用的空间是不是有这可用性,空间的释放是很有必要的。
二、结构体的内存对齐
内存对齐是指数据存储在内存中时,应该放置在地址是数据项大小的整数倍的内存位置上。
结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8;Linux中的默认值为4;
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
存在内存对齐的原因:
1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
如果说没有进行内存对齐,那么存储的时候会很方便,直接紧挨着存储就可以了,但是读取的时候,因为编译器是按照类型大小来读取相应的字节,像struct s1,没有进行内存对齐的话,就只能读取前四个字节和后四个字节,然后从前四个字节中取后三个和后四个字节中的第一个字节进行拼接,这样就很容易出问题,而且时间复杂度太高。而在计算机内部,就是用空间来换取时间。
所以,s1结构体的大小为4+4+1=9,s2结构体的大小为4+4=8;同样的变量类型,这就提醒我们以后再写结构体的时候,要注意,把占字节数小的类型都放在前面,同种类型的变量要定义完之后再开始定义下一种类型的变量。
枚举
个人觉得枚举的用处比较小,写代码的时候看着确实比较方便,但是在打印的时候就是自己写的坑,哭着也要填满。
枚举呢,顾名思义就是列举,C语言中已知的变量类型就只有整形,浮点型和字符型,要是我们想表示一些颜色啊,星期啊,性别什么的就有点困难了,这就要用枚举来实现。
简单表示一个性别吧
typedef enum SEX
{
man,
woman,
}SEX;
枚举如果不赋值的话,第一个变量默认是0,下面的变量逐渐递加,就是man 的值是0,woman 的值是1。
对于这个坑呢,用户在输入和电脑打印的时候,就要再加一个判断的代码了
输出
switch (pinfo->_sex)
{
case man:
printf("man\n");
break;
case woman:
printf("woman\n");
break;
default:
printf("无法识别\n");
break;
}
输入
while (1)
{
scanf("%d", &i);
if (i == 0 || i == 1)
{
pinfo->_sex = i;
break;
}
else
{
printf("请输入正确的信息!\n");
}
}
感觉很麻烦,好处就是看上去舒服。。。。。