结构体
1、基本定义
注:①结构体不可以自引用!但可以在结构体内定义该结构体类型的指针!
②定义结构体本质是新增一种类型;
③结构体传参要传结构体地址(指针),以此提高效率。
struct node1{
int a;
int b;
};
typedef struct node2 {
int c;
int d;
}node2;//通过typedef为结构体变量定义一个别名node2,在以后使用中可以使用别名以此提高编写效率
int main(){
struct node1 s;//用结构体定义变量
node2 q;//用别名定义变量
}
**注:**结构体不可以自引用!但可以在结构体内定义该结构体类型的指针!
struct Node
{
int data;
struct Node next;
};//错误
struct Node
{
int data;
struct Node* next;
};//正确
typedef struct Node
{
int data;
struct Node* next;
}Node;//正确
2、结构体变量的定义和初始化
struct Point1
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
typedef struct Point2
{
int x;
int y;
}p;
p p1;
p p2;
在定义结构体变量时,可以在初始化的部分定义其内容,也可以在之后定义。
typedef struct Point2
{
int x;
int y;
}p;
p p1;
p1.x=1;
p1.y=2;//可以直接用结构体类型的变量进行定义
typedef struct Point2
{
int x;
int y;
}p;
p* p2=(p*)malloc(sizeof(p));
p2->x=1;
p2->y=2;//定义一个指向结构体的指针并为其分配空间即可进行定义
3、结构体的内存对齐(结构体的占用大小的计算)
结构体内存空间占用的大小并不是单纯的元素相加,而是通过浪费一定量的空间来换取目标数据读取速度的加快
计算方式:
① 第一个成员在与结构体变量偏移量为0的地址处。
② 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
(起始偏移量要能整除该成员的对齐数)
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
③ 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
④ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处(即结构体大小),结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
样例:
struct S1
{
char a;
int b;
char C;
};
printf("%d\n", sizeof(struct S1));
1
2
3
4
5
6
7
char 为1个字节, int 为4个字节;
char a 从0偏移开始,占用一个字节;偏移量为1,接下来存放 int b,偏移量1不是对齐数4 的整数倍,所以向后继续偏移一直到4,4是对齐数4的整数倍,所以将int b存放在偏移地址为4处,占用4个字节;偏移量变为8,存放 char c ,占一个字节,偏移量9不是最大对齐数4的整数倍,所以向后继续偏移直到偏移处为12的地方。
自主设置默认对齐数
#pragma pack(a)//通过该指令可设置默认对齐数为a
1
位断
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 、signed int或者char(同属于整型家族);
2.位段的成员名后边有一个冒号和一个数字。
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
注: ① 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型;
② 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的;
③ 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段;
④位断不需要考虑内存对齐问题所以较为节省空间。
总而言之, 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
枚举
即一一列举
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun //最后一个不加逗号
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
与宏定义相比枚举的优点:
① 增加代码的可读性和可维护性;
② 和#define定义的标识符比较枚举有类型检查,更加严谨;
③ 防止了命名污染(封装);
④ 便于调试;
⑤ 使用方便,一次可以定义多个常量 。
联合体
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体), 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;
注:联合体需要考虑内存对齐,要求为最大内存数的整数倍。
动态内存管理
程序开始运行后在堆上开辟大量空间(数组之类的空间开辟在栈上进行),而在堆上开辟的空间使用完毕后需要在使用完成后由free函数进行释放,然后令指向该空间的指针指空,如果只申请不释放会造成内存泄漏问题。
动态申请空间主要涉及三个函数:malloc函数,calloc函数,relloc函数。
void* malloc (size_t size);
1
只申请空间,不对空间进行初始化,传入的参数size为要开辟的空间大小;
void* calloc (size_t num, size_t size);
1
申请空间,与malloc唯一的不同之处在于calloc会初始化为0,传入的参数size为单个空间的大小,参数a为所需要的单个空间的数量;
void* realloc (void* ptr, size_t size);
1
将分配size个大小的空间,然后在调整原内存空间大小的基础上,将原来内存中的数据移动到新的空间,返回值为调整之后的内存起始位置。
由于realloc可能会申请失败返回NULL所以不建议直接用原指针接收返回地址,正确使用方法为:
int* ptr = (int*)malloc(100);
int* p = NULL;
p = realloc(ptr, 1000);
if (p = !NULL) {
ptr = p;
}
内存释放操作
int* p=(int*)malloc(100);
......
free(p);
p = NULL;
柔性数组
1、定义
在结构体内大小为0(a[0])或空(a[])的数组(必须为结构体内最后一个元素且不能是唯一元素)
这样可以在结构体内具有一个变长数组包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配
的内存应该大于结构的大小,以适应柔性数组的预期大小 ,sizeof 返回的这种结构大小不包括柔性数组的内存 。
2、使用方法
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4
//初始化
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);