本文共 4486 字,大约阅读时间需要 14 分钟。
【结构体】
结构体是将具有不同或者相同元素类型放在一起的聚合类型。
一,结构体类型的声明
struct tag//struct是关键字,tag是标签,要见名知意。这里tag可省略,但不建议省略。{member_list;//声明一种类型,在c语言中,这里不能为空。}variable_list;//结构体变量列表,变量可以有多个。这里建议省略,需要用的时候再定义。
注意{};,后面的分号不能丢。
看下面一个声明:
struct A{ int a; char b; float c;}a[20],*p;struct B{ int a; char b; float c;}x;p = &x;
结果有报错:
编译器会把上面两个声明当成两个完全不同的两个类型,所以是非法的。
二,结构体成员
结构体成员可以是标量,数组,指针,也可以是其他结构体。
那么结构体是怎样访问成员呢?
结构体变量的成员是通过点操作符(.)或者—>访问的.
举个例子:
#include#include struct Test{ int a; char b; float c;};int main(){ struct Test A; struct Test *p; A.a = 10;//使用.访问a成员 A.b = 'B';//使用.访问b成员 p = &A; (*p).a = 20; (*p).b = 'b'; printf("a=%d b=%c", (*p).a, (*p).b); printf("\n"); printf("a=%d b=%c", p->a, p->b); system("pause"); return 0;}
最后结果是
三.结构体的自引用:
在结构体中包含一个类型为该结构体本身的成员可以吗?
先来试一试:
struct Node{ int data; struct Node next;};
编译显示
编译显示“next”没有被定义,可见直接在结构体中定义一个该结构体本身的变量是不合法的,正确的定义应该如下:
struct Node{ int data; struct Node *next;//定义成指针};
四,结构体的不完整声明
在结构体套结构体使用时,在不声明的情况下,两个相互套用,虽然在vs2013下能够通过运行,但是最好还是提前声明。
struct B;struct A{ int _a; struct B* pb;};struct B{ int _b; struct A* pa;};
五,结构体变量的定义和初始化(类比数组)
//声明:struct Test{ int a; char b;};//定义:struct Test p1;struct Test p2;//初始化://p1 = {a,b}//这种初整体始化的方式是不允许的,类比数组;struct Test p1 = {20,'a'};
struct Node{ int d[10]; struct Test; struct Node *next; };struct Node n = { { 1, 2, 3 }, { 20, 'a' }, NULL };//结构体嵌套初始化
六,结构体内存对齐
前面了解了结构体的概念,那么结构体的大小如何计算呢?它是是不是也可以类比数组呢?经过验证我们发现,当结构体内声明的类型顺序不一样时,结构体的大小也不一样。所以,计算结构体大小不能类比数组,这里还需要考虑内存对齐问题。
1.为什么存在内存对齐呢?
(1)、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(2)、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
2.结构体的内存对齐规则:
【1】.第一个成员在与结构体变量偏移量为0的地址处。
【2】.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8 linux中的默认值为4【3】.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。【4】.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。现在来看一个例子:
struct S1{ char c1; int i; char c2;};
结构体s1里的char c1的对齐数是1,第一个成员默认偏移量为0;int i的对齐数是4,1不能整除自身对齐数,所以得加上偏移量3;char c2的对齐数是1,8能够整除自身对齐数,就不需要加偏移量;最后看结构体总的大小为9,不能整除结构体中最大对齐数4,所以加偏移量3,最后结构体大小为12.
用图形理解如下:
结构体嵌套问题:
struct S1{ char c1;//1+3 int i;//4+4 char c2;//8+1};//12struct S2{ int a[5];//20 struct S1 s1;//12+20 double d;//8};//40
结构体s2里,int a[5]的对齐数为20;然后嵌套了结构体S1,嵌套的结构体对齐到自己的最大对齐数的整数倍处,这里20能够整除最大对齐数4,所以不需要偏移量;double d 对齐数是8,32能够整除8,;结构体总大小为40,能够整除最大对齐数8;最后S2的大小为40.
七,结构体传参
结构体传参传指针。
#include#include struct S{ int date[1000]; int num;};void print(struct S *s){ printf("%d\n", s->num);//20}int main(){ struct S s = { { 1, 2, 3, 4 }, 20 }; print(&s); system("pause"); return 0;}
【枚举】
一,枚举的定义
//一般的定义方式如下:enum enum_type_name{ENUM_CONST_1,ENUM_CONST_2,...ENUM_CONST_n} enum_variable_name;
注意:enum_type_name是自定义的一种数据数据类型名,而enum_variable_name为
enum_type_name类型的一个变量,也就是我们平时常说的枚举变量。实际上enum_type_name类型是对一个变量取值范围的限定,而花括号内是它的取值范围,即enum_type_name类型的变量enum_variable_name只能取值为花括号内的任何一个值,如果赋给该类型变量的值不在列表中,则会报错或者警告。ENUM_CONST_1、ENUM_CONST_2、...、ENUM_CONST_n,这些成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。------------------- ---摘录《c语言深度解剖》
二,枚举的初始化
enum Color{ RED, BIACK, BLUE,}; int main(){ enum color c; c = BLUE; printf("%d\n", RED);//0 system("pause"); return 0;}
{}里的内容是枚举类型的可取值,也叫枚举常量,这些可取值都是有值的,默认从0开始,一次递增1.
在定义的时候也可以赋值(枚举常量)。
三,下面再看看枚举与#define宏的区别:
1),#define宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
2),一般在编译器里,可以调试枚举常量,但是不能调试宏常量。3),枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。【联合体】
一,联合体类型的声明,定义与初始化
//声明union Un{ int a; float b; char c;};int main(){ union Un x;//定义 x.a = 25; printf("%d\n", x.a ); x.b=3.14; printf("%lf\n", x.b); //3.140000 x.c = 'c'; printf("%c\n", x.c); system("pause"); return 0;}
二,联合的特点
在union中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。这样一个联合变量的大小,至少是最大成员的大小。
看一个例子:
union Un{ int i; char c;};int main(){ union Un un; printf("%d\n", &(un.i)); printf("%d\n", &(un.c)); un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i); //11223355 system("pause"); return 0;}
编译后,我们发现,结构体成员的地址都是相同的.
现在看看内存:
对union型的成员的存取都是相对于该联合体基地址的偏移量为0处开始,也就是联合体的访问不论对哪个变量的存取都
是从union的首地址位置开始。然后根据大端小端判断,计算机是小端模式,所以结果是0x11223355。【判断当前计算机的大小端存储】
int check_sys(){ int i = 1;//0000 0000 0000 0000 0000 0000 00001 return (*(char *)&i);//对&i解引用就是首地址;}int main(){ int ret = check_sys(); if (ret == 1) { printf("xiaoduan\n"); } else { printf("daduan\n"); } system("pause"); return 0;}
看内存,低位保存在低地址中,编译后结果也是小端。
三,联合体大小是计算
union Un{ char c[5];//5 int i;//4};int main(){ printf("%d\n",sizeof(union Un));//8 system("pause"); return 0;}
联合体大小至少是最大成员的大小;当最大成员不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。