C语言基础(19)-结构体,联合体,枚举和typedef
一.结构体
1.1 结构体struct定义及初始化
#include <stdio.h> // 这个头文件在系统目录下 #include <stdlib.h> // 使用了system函数 #include <Windows.h> // 结构体简单使用 void structUseDemo(void); // 输出student结构体的内容 void print_student(struct student st); // 说明一种结构体类型 struct student { char name[20]; // << 姓名 int age; // << 年龄 int sex; // << 性别 int class_id; // << 班级ID }; int main() { structUseDemo(); system("pause"); return 0; } void structUseDemo(void) { // 定义结构体变量后赋值 struct student st1; strcpy(st1.name,"张三"); st1.age = 20; st1.sex = 1; // 0代表男,1代表女 st1.class_id = 1010; print_student(st1); printf("------------------------------------\n"); // 定义一个结构体变量,同时初始化它的内容 struct student st2 = {"奥巴马",60,3,1010}; print_student(st2); printf("------------------------------------\n"); struct student st3 = {"希拉里"}; // 只初始化第一个成员,后面都是0 print_student(st3); printf("------------------------------------\n"); struct student st4 = {0}; // 所有成员都初始化为0 print_student(st4); printf("------------------------------------\n"); struct student st5 = {.sex = 1,.name = "马英九"}; print_student(st5); } void print_student(struct student st){ printf("name=%s\n", st.name); printf("age=%d\n", st.age); if (st.sex == 0) { printf("sex=%s\n", "男"); } else { printf("sex=%s\n", "女"); } printf("class_id=%d\n",st.class_id); }
执行结果:
1.2 结构体的内存对齐
编译器在编译一个结构的时候总是采用内存对齐模式,结构体总是以最大的成员作为对齐单位,以偶数位对齐。
如果结构体的所有成员都是同一种类型,那么这个结构体在内存和数组的存放方式是一样的。
#include <stdio.h> // 这个头文件在系统目录下 #include <stdlib.h> // 使用了system函数 #include <Windows.h> // 结构体的简单使用1 void structUseDemo1(); // 结构体当中的所有成员在内存当中都是连续存放的 struct A { int a1; int a2; }; struct B { char a2; // 结构体成员是要对齐的 int a1; }; struct C{ char a1; char a2; int a3; }; struct D{ char a1; int a3; char a2; }; struct F { char a1; short a2; char a3; int a4; }; struct H { char a1; short a2; int a3; short a4; char a5; }; int main() { structUseDemo1(); system("pause"); return 0; } void structUseDemo1(){ struct A a = {1,2}; // 定义并且初始化 int *p = (int *)&a; printf("%u\n",sizeof(a)); // 输出结果为8 printf("%d\n",*p); printf("%d\n",p[1]); printf("----------- struct B -------------\n"); struct B b = {1,2}; printf("%u\n",sizeof(b)); // 输出结果为8 printf("------------- struct C -----------\n"); struct C c = { 0 }; printf("%u\n",sizeof(c)); printf("------------ struct D ------------\n"); struct D d = {0}; printf("%u\n",sizeof(d)); // 输出结果为12 !!!:因此struct C的写法比struct D的写法更合理 printf("------------ struct F ------------\n"); struct F f = {1,2,3,4}; printf("%p\n",&f); printf("%u\n",sizeof(f)); // 输出结果为12,结构体的对齐总是以偶数位进行对齐 }
执行结果:
内存对齐示意图:
1.3 指定结构体元素的位字段
定义一个结构体的时候可以指定具体元素的位长
struct test{ char a : 2;//指定元素为2位长,不是2个字节长 };
1.4 结构体数组
可以使用下列两种形式来定义结构体数组
void structArrayUseDemo() { struct student st[3]; // 定义一个结构体变量数组 strcpy(st[0].name,"张三"); st[0].age = 50; st[0].sex = 0; st[0].class_id = 1; strcpy(st[1].name, "李四"); st[1].age = 60; st[1].sex = 0; st[1].class_id = 2; strcpy(st[2].name, "王五"); st[2].age = 70; st[2].sex = 0; st[2].class_id = 3; for (int i = 0; i < 3; i++) { print_student(st[i]); printf("--------------------------------\n"); }; } void sturctArrayUseDemo1(){ //struct student st[5] = { 0 }; // 将所有成员都置为0 struct student st[5] = { { "张三", 30, 0, 90,0}, { "李四", 40, 1, 89,1 }, { "王五", 60, 1, 23,1}, { "赵六", 55, 0, 81 ,0}, { "陈七", 35, 0, 100,1} }; for (int i = 0; i < 5; i++) { print_student(st[i]); printf("--------------------------------\n"); }; }
1.5 结构体嵌套结构体,结构体赋值及结构体指针
例1:结构体嵌套结构体赋值:
#include <stdio.h> #include <stdlib.h> #include <windows.h> #pragma warning(disable:4996)
struct B {
int a1;
struct A a2; // 这个结构体的成员是另一个结构体
};
struct C {
int a1;
struct B a2;
};
static void structUseDemo(); static void structUseDemo() { struct B b; b.a1 = 10; b.a2.a1 = 0; struct C c; c.a2.a2.a1 = 0; printf("%u\n",sizeof(struct B)); }
例2:结构体变量赋值
#include <stdio.h> #include <stdlib.h> #include <windows.h> #pragma warning(disable:4996) static void structUseDemo1(); // 结构体变量赋值 static void structUseDemo1() { struct man m1 = {"圣堂刺客",20}; struct man m2 = m1; printf("m1=%s\n",m1.name); printf("m2=%s\n", m2.name); struct man *p = &m1; //(*p).age = 100; //strcpy((*p).name,"风暴之灵"); p->age = 100; strcpy(p->name,"风暴之灵"); printf("m1.name=%s\n",m1.name); printf("m1.age=%d\n", m1.age); }
例3:结构体指针
#include <stdio.h> #include <stdlib.h> #include <windows.h> #pragma warning(disable:4996) static void structUseDemo2(); static void structUseDemo2() { struct man m[10] = {0}; struct man *p = &m; // p指向了数组m的首元素地址 p[0].age = 1; strcpy(p[0].name,"帕克"); p++; p->age = 100; strcpy(p->name,"莉娜"); p++; p->age = 20; strcpy(p->name, "露娜"); p = m; // 将指针归位 /*for (int i = 0; i < 10;i++) { printf("name=%s,age=%d\n",m[i].name,m[i].age); }*/ for (int i = 0; i < 10;i++) { printf("name=%s,age=%d\n",p->name,p->age); p++; } }
1.6 在堆中创建结构体
#include <stdio.h> #include <stdlib.h> #include <string.h> #pragma warning(disable:4996) void structUseDemo4(void); void structUseDemo4(void) { struct women *p = calloc(1, sizeof(struct women)); p->name = calloc(100,sizeof(char)); strcpy(p->name,"漩涡鸣人"); p->age = 30; printf("当前p的名称为:%s,年龄为:%d\n",p->name,p->age); free(p->name); // 一定要先翻译p->name,如果先释放p,会导致内存泄露 free(p); }
运行结果:
1.7 结构体作为函数的参数
下面有两个方法用于打印结构体成员变量的值
// 结构体作为函数的参数 void structUseDemo5(struct man m) { //在栈中会有类似这样的代码m = a; printf("%s,%d\n",m.name,m.age); } // 1.不要把结构体作为函数的参数传递进去,因为这涉及到形参的拷贝,推荐传递结构体的指针 // 2.由于传递的是结构体的地址,const可以保护实参的值不被修改 void structUseDemo6(const struct man *m) { printf("%s,%d\n",m->name,m->age); }
二.联合体
联合union是一个能在同一个存储空间存储不同类型数据的类型。
联合体所占的内存长度等于其最长成员的长度,也有叫做共用体。
联合体虽然可以有多个成员,但同一时间只能存放其中一种。
联合体变量的任何一个成员赋值,都会影响到其它成员。
三.枚举
enum mysex { man = 5, woman }; // 说明了一个枚举 ,可以为其指定一个默认值,man = 5,woman就是6,依此类推 void enumUseDemo1(void) { struct man m; strcpy(m.name,"佐助"); m.age = 30; m.sex = man; printf("%d\n",man); } // 输出为5.
四.typedef
typedef是一种高级数据特性,可以用于定义一个新的数据类型
typedef unsigned char BYTE
1与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值。
2typedef是编译器处理的,而不是预编译指令
3typedef比#define更灵活
直接看typedef好像没什么用处,使用BYTE定义一个unsigned char。使用typedef可以增加程序的可移植性。
typedef struct man M; // 说明了一个新的数据类型,名字叫M typedef unsigned char BYTE; // 说明了Byte这种数据类型 typedef short NUM; void typedefUseDemo(void) { M m; m.age = 10; BYTE a; }