一、结构体
定义结构体变量的方式:
1)先声明结构体类型再定义变量名
2)在声明类型的同时定义变量
3)直接定义结构体类型变量(无类型名)
使用方法:
//结构体类型的定义
struct stu
{
char name[50];
int age;
};
//1.先定义类型,再定义变量(常用)
struct stu s1 = { "mike", 18 };
//2.定义类型同时定义变量,有结构体名称
struct stu2
{
char name[50];
int age;
}s2 = { "lily", 22 };
//3.定义类型同时定义变量,无结构体名称
struct
{
char name[50];
int age;
}s3 = { "yuri", 25 };
//4.使用点运算符.改变结构体内成员
s3.age=18;
结构体内存对齐:
每个类型都会有自己的内存,比如int类型占4个字节,那么一个结构体占多少内存呢?这个是要看结构体内部的成员结构的。
在结构体中,会存在一种内存对齐机制:
1)各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数
2)结构体所占用内存必须是字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数
struct A
{
char a; //偏移量为0,满足对齐方式,a占用1个字节;
double b; //下一个可用的地址的偏移量为1,不是sizeof(double)=8
//的倍数,需要补足7个字节才能使偏移量变为8(满足对齐
//方式),因此VC自动填充7个字节,b存放在偏移量为8
//的地址上,并且它自己占用8个字节。
int c; //下一个可用的地址的偏移量为16,是sizeof(int)=4的倍
//数,满足int的对齐方式,所以不需要VC自动填充,存
//放在偏移量为16的地址上,并且它自己占用4个字节。
//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构
//的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof
//(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为
//sizeof(double)=8的倍数。
};
结构体内存大小为24
若是使用了#pragma pack(n),则规则变为:
1)对结构体中各变量逐一对齐。通过逐个比较变量大小和指令中n的值,选择两者最小值(或其倍数)作为内存偏移量。
2)结构体中所有变量对齐后,结构体的大小应该是struct中最大成员长度和n两者中的最小值的倍数,若不满足则在最后一个变量后插入空字符以满足规则。
#pragma pack(4)
struct A
{
char a; //sizeof(char)与4的最小值是1,因此对齐值为4,偏移量为0
//(对齐值整数倍),并且a占用1个字节;
double b; //sizeof(double)与4的最小值为4,因此对齐值为4,下一个可用
//的地址的偏移量为1,不是4的倍数,需要补足3个字节才能使偏移量
//变为4(对齐值整数倍),因此VC自动填充4个字节,b存放在偏移量为4
//的地址上,并且它自己占用8个字节。
int c; //sizeof(int)与4的最小值为4,因此对齐值为4,下一个可用的地址
//的偏移量为12(对齐值整数倍),所以不需要VC自动填充,c存
//放在偏移量为12的地址上,并且它自己占用4个字节。
char d; //sizeof(char)与4的最小值是1,因此对齐值为4,下一个可用
//的偏移量为16(对齐值整数倍),所以不需要VC自动填充,d存
//放在偏移量为16的地址上,并且它自己占用1个字节。
//结构体的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof
//(double)=8)与4的最小值为4,因此结构体的最大对齐值为4
//所有成员变量都分配了空间,空间总的大小为1+3+8+4+1=17,不是结构
//的最大对齐值4的倍数,所以需要填充3个字节,以满足结构的大小为最大对齐值4倍数。
};
结构体内存大小为20
二 、共用体(联合体)
联合union是一个能在同一个存储空间存储不同类型数据的类型
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
内存大小:联合体所占的内存长度等于其最长成员的长度倍数
地址:共用体变量的地址和它的各成员的地址都是同一地址。
#include <stdio.h>
union Test
{
char a;
int b;
short c;
};
int main()
{
union Test tmp;
printf("%p, %p, %p\n", &(tmp.a), &(tmp.b), &(tmp.c));
printf("%d\n", sizeof(union Test));
tmp.b = 10;
printf("%d\n", tmp.a);
printf("%d\n", tmp.c);
tmp.a = 'a';
printf("short: %d\n", tmp.c);
printf("int: %d\n", tmp.b);
return 0;
}
运行结果:
三、枚举
将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
- 枚举值是常量,不能在程序中用赋值语句再对它赋值。
- 举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …
- 未赋值的元素会从前面最靠近自身的,有赋值的元素开始按顺序默认赋值
#include <stdio.h>
enum Test1
{
a, b, c, d
};
enum Test2
{
q = 5, w, e, r
};
int main()
{
enum Test1 A;
A = a;
printf("%d ", A);
A = b;
printf("%d ", A);
A = c;
printf("%d ", A);
enum Test2 B;
B = q;
printf("%d ", B);
B = w;
printf("%d ", B);
B = e;
printf("%d ", B);
return 0;
}
执行结果:0 1 2 5 6 7
四、typedef
typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。
- 与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值
- #define发生在预处理,typedef发生在编译阶段
#include <stdio.h>
typedef int INT;
typedef char BYTE;
typedef BYTE T_BYTE;
typedef unsigned char UBYTE;
typedef struct type
{
UBYTE a;
INT b;
T_BYTE c;
}TYPE, *PTYPE;
int main()
{
TYPE t;
t.a = 254;
t.b = 10;
t.c = 'c';
PTYPE p = &t;
printf("%u, %d, %c\n", p->a, p->b, p->c);
return 0;
}