枚举
枚举类型的定义
enum Day//星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜色 { RED, GREEN, BLUE };
在枚举里面,每个常量后面用逗号不用分号
枚举常量是有值的,枚举的取值从0开始,一次往下增1
枚举常量的数值可以进行修改,当用枚举创建一个变量时才会占用空间
枚举的优点
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
#define定义的参数,调试时看不到参数具体内容,会直接完成替换,调试过程中看不到M的值,m会直接被替换成M
C语言下编译器不会报错,C++语法下编译器会报错
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
联合(共用体)
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)。
比如:
//联合类型的声明 union Un { char c; int i; }; //联合变量的定义 union Un un;
这里是4,是因为共用同一块空间
c所用空间为1
i所用空间为1,2,3,4
联合体的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
用联合体进行大小端判断
int check_sys() { union Un { char c; int i; }u; u.i = 1; return u.c; } int main() { if (check_sys()) printf("小端"); else printf("大端"); return 0; }
联合大小的计算
union Un { char arr[5]; int i; }; int main() { printf("%d\n", sizeof(union Un)); return 0; }
这里发生了对齐,arr每个元素类型是char,占一个字节,对齐数为1,i对齐数为4,所以答案是8
这里arr占1,2,3,4,5,i占1,2,3,4,由于要对齐所以多开辟了三个空间,只不过没用而已
这里a1,a2,a3,a4,a5都占用一个字节就是1,i占用四个字节,1,2,3,4
数组在创建时会一次性占用自身大小的字节数,而不是数组内容公用一个空间
short数组占用1-14,i占用1,2,3,4,要对齐即4的倍数,所以占用16个字节 ,short数组的对齐数是2,
习题1
在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是( )
struct A { int a; short b; int c; char d; }; struct B { int a; short b; char c; int d; };
16和12
两个结构体都是向int看齐。结构体A中,a独自对齐一个4字节,b+c超过了4字节,所以b独自对齐一个4字节,c独自对齐一个4字节,剩下一个d独自对齐一个4字节,共16字节。结构体B中,a独自对齐一个四字节,b+c+d才超过了4字节,所以b和c一起对齐一个4字节,d单独对齐一个4字节,共12字节,故选C。
习题2
下面代码的结果是:( )
#pragma pack(4)/*编译选项,表示4字节对齐 平台:VS2013。语言:C语言*/ int main(int argc, char* argv[]) { struct tagTest1 { short a; char d; long b; long c; }; struct tagTest2 { long b; short c; char d; long a; }; struct tagTest3 { short c; long b; char d; long a; }; struct tagTest1 stT1; struct tagTest2 stT2; struct tagTest3 stT3; printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3)); return 0; } #pragma pack()
12 12 16
三个结构体都向最长的4字节long看齐。第一个a+d+b才超过4字节,所以a和d一起对齐一个4字节,剩下两人独自占用,共12字节,第二个同理c,d合起来对齐一个四字节,也是12字节。第三个因为c+b,d+a都超过4字节了,所以各自对齐一个4字节,共16字节。故选A。
习题3
有如下宏定义和结构定义
#define MAX_SIZE A+B struct _Record_Struct { unsigned char Env_Alarm_ID : 4; unsigned char Para1 : 2; unsigned char state; unsigned char avail : 1; }*Env_Alarm_Record; struct _Record_Struct *pointer = (struct _Record_Struct*)malloc (sizeof(struct _Record_Struct) * MAX_SIZE);
当A=2, B=3时,pointer分配( )个字节的空间。
9
说明:结构体向最长的char对齐,前两个位段元素一共4+2位,不足8位,合起来占1字节,最后一个单独1字节,一共3字节。另外,#define执行的是查找替换, sizeof(struct _Record_Struct) * MAX_SIZE这个语句其实是3*2+3,结果为9,故选D。
习题4
下面代码的结果是( )
int main() { unsigned char puc[4]; struct tagPIM { unsigned char ucPim1; unsigned char ucData0 : 1; unsigned char ucData1 : 2; unsigned char ucData2 : 3; }*pstPimData; pstPimData = (struct tagPIM*)puc; memset(puc,0,4); pstPimData->ucPim1 = 2; pstPimData->ucData0 = 3; pstPimData->ucData1 = 4; pstPimData->ucData2 = 5; printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]); return 0; }
02 29 00 00
puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,至此AD排除,然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即0x29,故选B。
习题5
在VS2013下,默认对齐数为8字节,这个结构体所占的空间大小是( )字节
typedef struct{ int a; char b; short c; short d; }AA_t;
A.16
B.9
C.12
D.8
12
习题6
#include <stdio.h> union Un { short s[7]; int n; }; int main() { printf("%d\n", sizeof(union Un)); return 0; }
A.14
B.4
C.16
D.18
结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节
习题7
在X86下,小端字节序存储,有下列程序
#include<stdio.h> int main() { union { short k; char i[2]; }*s, a; s = &a; s->i[0] = 0x39; s->i[1] = 0x38; printf(“%x\n”,a.k); return 0; }
输出结果是( )
A.3839
B.3938
C.380039
D.不确定
union只有2字节,2字节的十六进制只有4位,所以答案CD排除。而位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839,故选A。
习题8
关于动态内存函数的说法错误的是:( )
A.malloc函数向内存申请一块连续的空间,并返回起始地址
B.malloc申请空间失败,返回NULL指针
C.malloc可以向内存申请0字节的空间
D.malloc申请的内存空间,不进行释放也不会对程序有影响
D
说明:不释放会产生内存碎片,小型程序可以不关注,但是在中大型程序上影响极其深刻。故选D。AB是函数的基本功能,C选项比较特殊,malloc(0)是允许的,也会返回一个指针,只是没有空间所以不可使用而已。
习题9
下面代码的结果是:( )
enum ENUM_A { X1, Y1, Z1 = 255, A1, B1, }; enum ENUM_A enumA = Y1; enum ENUM_A enumB = B1; printf("%d %d\n", enumA, enumB);
A.1, 4
B.1, 257
C.2, 257
D.2, 5
枚举默认从0开始,所以X1是0,故Y1是1,给了数字后会根据数字向后推,那么Z1是255,A1是256,所以B1是257,故选B。
习题10
关于动态内存相关函数说法错误的是:( )
A.malloc函数和calloc函数的功能是相似的,都是申请一块连续的空间。
B.malloc函数申请的空间不初始化,calloc函数申请的空间会被初始化为0
C.realloc函数可以调整动态申请内存的大小,可大可小
D.free函数不可以释放realloc调整后的空间
realloc在操作过程中是释放旧空间分配并返回新空间,所以返回的新空间也是需要释放的,故选D。AB是malloc和calloc的区别。C是realloc的基础功能。