目录
一:union关键字
二:enum关键字
三:typedef关键字
一:union关键字
联合关键字:联合体内部共享空间,整个union的大小由内部最大的元素决定。
#includeunion un { int a; char b; // a和b共用一个空间,不过空间总大小由a决定 }; int main() { // a和b共用一个空间,不过空间总大小由a决定 printf("%dn", sizeof(union un)); // 4 return 0; }
联合体的访问和结构体类似都可以采用 . *** 作符或 -> 指向 *** 作符。
union un x; x.a = 10; union un* p = &x; p->a;
union 的空间布局问题:
联合体的空间开辟由元素最大的为准,在这里由a决定。四字节,那b占用的是a的低地址处还是高地址处呢?
任何变量在开辟空间的时候,这个变量开辟后都有地址,这个地址一定是众多字节中最小的
我们将联合体变量的地址和联合体内最大元素的地址打印出来看看:
不难看出,在数值大小上是一样的
对于b来说,在申请空间的时候,所有的空间申请都是由较低地址处向上开始分配的,所以b是在最低地址开始的,换言之,内部成员b开辟的空间和联合体本身和a变量的地址值是一样的。
结论:
联合体内所有成员的起始地址都是一样的,每一个都是第一个元素。
b永远在a的低地址处!!!
利用联合体的空间分布可以巧妙判断出大小端:
这里要把一组二进制序列 0x 00 00 00 01保存在a对应的空间里,此时四个字节每个都有地址,而地址具有高低之分,而我们的数据按字节对应的1bit位进行划分的时候,数据就有高低权值位之别,所以存储方案有两种,一种高权值位放在高地址处,低权值低地址处,如我们上图的左边存法,01放在低地址处,第二种方案相反,如上图右边将01放在高地址处,因为b永远在a的低地址处,b占一个字节,如上图x.b=1红色记号笔划分出,如果存储方案是第一种,那么b=1,如果是第二种,那么b=0,而第一种存储方案正式小端的存储法则,第二种正是大端的存储法则。
如代码展示:
#includeunion un { int a; char b; }; int main() { union un x; x.a = 1; if (x.b == 1) { printf("小端n"); } else { printf("大端n"); } return 0; }
注意:联合体的整大小必须能整除联合体内任何一个元素的大小
#includeunion un { int a; char b[5]; }; int main() { printf("%dn", sizeof(union un)); // 8 return 0; }
根据我们之前的认知,联合体空间由最大元素决定,b的大小是5,按理来讲联合体大小应该是5,但运行起来大小却是8,联合体的整大小必须能整除联合体内任何一个元素的大小
再看一段代码:
体现联合体和大小端的对应关系:
#include二:enum关键字union un { int i; char a[4]; }*p, u; int main() { p = &u; p->a[0] = 0x39; p->a[1] = 0x38; p->a[2] = 0x37; p->a[3] = 0x36; printf("0x%x", p->i); //0x36373839 return 0; }
enum枚举关键字作用:枚举一堆的常量,内部的常量直接可以被当作数据使用,枚举本身也是新增或设计了一种类型,换言之,我们可以使用枚举类型直接定义变量。如下:
#includeenum color { RED, YELLOW, BLACK, GREEN, BLUE }; int main() { enum color c = RED; printf("%dn", RED); // 0 printf("%dn", BLACK); // 2 printf("%dn", BLUE); // 4 return 0; }
枚举出来的本质就是整数,对应的就是某种字面值,不可被修改的,所以RED,BLACK,BLUE就是真正意义上的常量
为什么要存在枚举?
1:现实世界中,有一大堆具有相关性的常量需要被在代码中体现出来。
2:一旦我们枚举常量之后,所有常量的常量名不是数字而是直接用英文单词去代表,这样写出来的代码具有自描述性。
枚举常量的设定:
如果将第一个枚举常量的内容赋予一个特定的数字,那么后续的枚举常量会呈现加1式的递增,也可以分段式递增:
#include三:typedef关键字enum color { RED=10, YELLOW, BLACK=-9, GREEN, BLUE }; int main() { enum color c = RED; printf("%dn", RED); // 10 printf("%dn", YELLOW); // 11 printf("%dn", BLACK); // -9 printf("%dn", GREEN); // -8 printf("%dn", BLUE); // -7 return 0; }
本质:类型重命名。
#include//(1) // 对结构体类型进行重命名 typedef struct stu { char name[16]; int age; char sex; }stu_t; //(2) //对unsigned int 重命名 u_int 简化 typedef unsigned int u_int; //(3) //对指针int*重命名 typedef int* int_p; //(4) typedef int a[10]; // 此刻a相当于一种数组类型 int main() { u_int x = 0; int_p p = NULL; stu_t s; a b; return 0; }
类型重命名,可以对一些不太好理解的数据类型进行简化。
但是也不是说typedef可以随便的重命名,如果对指针或者数组进行重命名的时候,那么使用的时候,就会忽略一些细节。比如说数组的元素有几个,类型是什么,指针是几维指针,什么类型的指针。过度的typedef本质其实变相就是一种让人困扰的东西,但是比较推荐大家在结构体的时候运用typedef关键字
typedef和#define的区别
先看代码:
此段代码用的是typedef
#includetypedef int* int_p; int main() { //int* a, b; // 此时a为指针,b为int 整型类型 //int* a = NULL, b = 0; //可读性太差 int_p a, b; // 此时a 和 b均为指针,等价于int* a, * b; return 0; }
千万不要把typedef类型重命名看作某种替换,不能直接把int_t理解为int*,二者不能直接替换。而应将int_t理解成一种全新的类型,所以就不存在*会和a先结合还是和b先结合的问题。这个*的类型会对其后的所有定义的变量全部起效,int_p就作为一种独立类型去使用。
再来用#define试试
#include#define ptr_t int* int main() { ptr_t a, b, c; //等价于 int* a, b, c; //a为int*, b和c均为int; return 0; }
结论:
typedef类型重命名,并不是本质的文本替换,形成了一个新的独立的类型
宏define做的是纯纯文本替换
案例:
#include#define INT32 int typedef int int32; int main() { unsigned INT32 a; //因为宏define是文本替换,所以INT32就相当于int,所以就是ungsigned int a; //unsigned int32 b; 代码报错 return 0; }
问题:typedef static int int32_t 行不行?
从图中很容易看出是不行的,此时编译器出错。
在32个关键字中,有五个存储类型关键字:
而存储类型关键字有个特点:
存储关键字,不可以同时出现,也就是说,在一个变量定义的时候,只能有一个。
所以typedef和static这两个关键字不能同时出现,所以上述代码会报错。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)