【结构体】
结构体是将具有不同或者相同元素类型放在一起的聚合类型。
一,结构体类型的声明
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<stdio.h>
#include<windows.h>
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
};//12
struct 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<stdio.h>
#include<windows.h>
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;
}
联合体大小至少是最大成员的大小;当最大成员不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。