
1.位段
首先要注意的是,位段是依托结构体存在的,即位段只能在结构体内使用。其次,位段的使用对象是比特位i,而不是字节。
看下面的代码。
可以看到,第一个结构体与第二个结构体的差别仅仅在于后者使用了位段。如何求结构体的字节我们已经很清楚了,那么如果加上了位段的话,即表示该变量只需占用指定比特位的空间。如在第二个结构体里,首先a是int类型,那么系统会为其开辟4个字节,即32个比特位的空间,但是后面的“:2”说明使用者只需要其占用2个比特位的空间,那么即使开辟了32个比特位,a也只会占用其中2个比特位的空间,这时还剩30个比特位。对b来说,它也只需要2个比特位的空间,于是在剩下的30个比特位中又拿出两个给了b,此时还剩28个比特位,依次类推,则实际上只用开辟一次空间就可以把四个变量储存起来(注意,虽然一共只占用了8个比特位,但是系统为其开辟的空间仍旧是32个比特位,其他空间则被浪费了。)因此第二个结构体的大小就是4个字节。
当然,也有可能只开辟一次空间不够的情况。
例如这种情况,系统首先为a开辟一个字节的空间,但a实际只占用了4个比特位,此时还剩5个比特位,接着b要占用1个比特位的空间,此时还剩4个比特位,但是c需要5个比特位的空间,此时就需要再开辟一个字节的空间,那么这里就有个问题,上一次剩下的4个比特位的空间会不会被使用呢?我们假设不会被使用,那么c占用了新开辟的8个比特位中的5个比特位的空间,此时还剩3个比特位,但是d需要6个比特位的空间,所以系统又为其开辟了一个字节的空间。因此该结构体的总大小就是三个字节。而如果系统不会浪费的话,那么2个字节就可以了。在vs2019下可以看到,其支持的是前者。
那么,位段到底要怎么使用呢?
举个例子,当我们定义一个人的年龄时一般用的是int类型,开辟了4个字节,我们知道一个int类型可以表示的最大整数为2^32-1,而一个人的年龄根本不可能那么大。所以我们可以使用位段,例如设置int age:10;即age这个变量占用十个比特位,我们知道此时age中可以表示的最大整数为2^10-1,1023,人的年龄也不可能超过1023的吧?因此使用位段就可以把原来占用的32个比特位缩减成10个比特位,就可以节省内存。
既如此,那还有一个问题,既然位段的单位是比特位,那么他们在内存中到底是怎么存储的呢?
我们给第二个结构体赋值进行调试。在对s初始化时其三个字节均是0,即00000000 00000000 00000000.我们知道3的二进制序列是011,但因为a占4个比特位,所以存储为0011。在这里,假设在一个字节内是从右往左开始存放的。那么现在就变成了00000011 00000000 00000000。b的二进制序列为101,但是其只占用了1个比特位,因此实际上只有最末端的1是有效的,即此时变成了00010011 00000000 00000000。以此类推,当程序运行后这三个字节就成了00010011 00001001 00001010,转化成十六进制代码则为13 09 0a。
枚举所谓枚举,就是将某一事物的所有可能取值列出来,例如星期,可以有星期一,二,三,四,五,六,七;月份,一共只有12个月。枚举定义的是常量。
要注意的是,枚举类型一次只可以使用其中一个取值,毕竟一天不可能同是星期一和星期二,系统会默认给第一个可能取值赋值为0,接下来就依次加1,但是也可以修改,比如在这里将TUES改为了3,则接下来的WED就为4了。
联合体所谓联合体,也可以理解为共用体,其中的成员共享一段存储空间,也就是说,后赋值的数会覆盖前赋值的数。
如图。该联合体的大小为4个 字节。
关于联合体大小的计算,要遵循以下两个原则:
联合体的大小至少要是最大成员的大小。如果联合体里有一个成员是int,那么该联合体至少要有4个字节的大小。当最大成员大小不是最大对齐数的整数倍时,其联合体大小要对齐到最大对齐数的整数倍。 动态内存首先我们要明白一点,为什么有动态内存管理?在内存中共有三块存储区域,分别为栈区,堆区,静态区,我们平常写的代码一般都是储存在栈区或者静态区,但是这样有两个缺点,一是其开辟的空间大小是固定的,而是在开辟是必须指定数组的长度。也就是说,我们无法在后期动态修改其大小。
说道动态内存,就必须要先介绍四个动态内存函数,分别是malloc,free,calloc,realloc。接下来,分别对上述四个函数进行介绍。
malloc这个函数的原型是void* malloc(size_t size)(括号里是所要开辟空间的总的字节大小)
可以看出,这个函数返回的是一个指针。如果开辟动态内存成功,则其返回的是该空间的地址,若是失败,则返回的是一个空指针。
2.free
这个函数的原型是void free(void* ptr)
如果ptr指向的空间不是动态内存分配的,那么free的行为是未定义的;如果ptr是空指针,则free什么也不做。
3.calloc
这个函数的原型是void* calloc(size_t num,size_t size)(括号里分别为元素个数与每一个元素的大小。与malloc的不同在于,calloc会自动初始化为0,如果开辟成功则返回内存地址,失败则返回空指针。
4.realloc
这个函数的原型是void* realloc(void* ptr,size_t size)(括号里分别为所要调整的内存地址,修改后的内存总大小)。调整成功则返回调整后的内存地址。
这个函数是为了更加方便灵活地使用我们的内存空间。
当然,在使用这个函数时有三种情况。一是可以在已有的空间基础上进行扩充,二是另外找一块空间,重新开辟。当然,也可能调整失败,返回了空指针。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)