自定义类型: 结构体 ,枚举, 共用(联合)体
自定义类型: 结构体 ,枚举, 共用(联合)体
目录
一. 结构体
结构体的声明
特殊的声明
结构体的自引用
结构体变量的定义和初始化
结构体传参
结构体内存对齐
修改默认对齐数
位段
二. 枚举
枚举的优点
枚举的定义
枚举的使用
三 .联合(共用)体
共用(联合)体的声明
共用(联合)的定义
共用(联合)的特点
共同(联合)体的大小
一. 结构体
结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型.
例如描述一个学生的信息:
struct Student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
我们声明的这个自定义类型为struct Student
特殊的声明
在声明结构的时候,可以不完全的声明。例如 :
struct{
int a;
char b;
float c;
}x;
struct{int a;
char b;
float c;
}a[20], *p;
上面的两个结构在声明的时候省略了结构体标签
我们可以看出这两个结构体内容是一样的, 那么这两个自定义类型是不是同一种类型呢 ?
在上面代码的基础上 , 我们来看这样一段代码
p = &x;
这段代码是不合法的 , 如图 :
原因是编译器把上面的两个声明当成了两个不同的类型 .
这样是不行滴, 这样会造成自递归, 一个包含一个就无穷尽了, 所以编译器会报错 .
错误的看完就该看正确的了, 正确的自引用如下:
struct w{
int a;
char b;
float c;
struct w* next;
};
初始化 :
struct score {
int chinese;
int math;
int english;
}s1 = { 88,89,80 };
struct score s2 = { 90,45,67 };
struct Student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
struct score stu;
}stu1 = { "张三",18,"男","12376",{ 88,89,80 } };//结构体嵌套初始化
struct Student stu2 = { "李四",20,"男","12378",{ 90,45,67 } };//结构体嵌套初始化
注意:
在声明我们需要的结构体类型后, 我们定义结构体变量时, 前面的类型名很长, 让人很不舒服 . 如下代码 :
struct student{
int a;
char b;
float c;
};
struct student s1;
struct student s2;
需要注意的是, 我们声明的自定义(结构体)类型是struct student, 并不是student .
我们可以用typedef来解决这个问题, 如下代码 :
代码1:
struct student{
int a;
char b;
float c;
};
typedef struct student stu;
struct student s1;
stu s2;
我们就可以用struct student的别名 stu 来声明s2 .
代码2:
typedef struct student{
int a;
char b;
float c;
}stu;
struct student s1;
stu s2;
typedef也可以加在结构体声明的最前面, 此时末尾的 “ ; “ 前不再是结构体变量的声明, 而是别名 .
我们在给函数传参数时, 有时会传值, 有时会传地址, 结构体也一样, 如下代码 :
#include<stdio.h>
#include<stdlib.h>
typedef struct MyStruct {
int arr[1000];
char str[10];
}S;
void print_1(S x) {
printf("%s\n", x.str);
}
void print_2(S* p) {
printf("%s\n", p->str);
}
int main() {
S a = { {1,2,3,4},"哈哈" };
print_1(a);
print_2(&a);
system("pause");
return 0;
}
运行如下:
代码中的函数 print_1 和 print_2 都可以完成打印的功能, 那么哪个更好呢 ?
答案是 print_2 , 因为函数传参的时候,参数是需要压栈(在栈中分配内存),会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大(如上面代码中的有1000个元素的数组),参数压栈的的系统开销比较大,所以会导致性能(效率)的下降, 而且在函数调用结束, 还有出栈(释放为形参分配的内存)的操作。
所以, 结构体传参的时候,传结构体的地址比较科学
(点击跳转)
二. 枚举
枚举在C/C++/C#中,是一个被命名的整型常数的集合
我们可以使用#define 定义常量,为什么非要使用枚举? 枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符相比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
像结构体一样, 也可以没有标签
enum {
零,
星期一,
星期二,
星期三,
星期四,
星期五,
星期六,
星期七
};
以上两种定义方式中, 枚举中的常量都没有被赋值, 他们的值默认从0开始,依次递增1,当然在定义的时候也可以赋初值, 例如 :
enum {
星期二 = 2,
星期一 = 1,
星期五 = 5,
星期六,
星期三 = 3,
星期四,
星期七 = 7
};
没有初值的枚举常量都值都是上一个加一 .
VS2017中
#include<stdio.h>
#include<stdlib.h>
#define M 4
enum day{
星期二 = 2,
星期五 = 5,
星期六,
星期三 = 3
};
enum day 星期七 = 星期二 + 星期五;
enum day 星期四 = M;
enum day 星期一 = 1;
/*
//不可以这样
enum day 星期一;
星期一 = 1;
*/
int main() {
int Mon = 星期一;
int num = 星期一 + 星期二;
printf("%d %d %d %d %d %d %d\n", 星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期七);
printf("Mon = %d, num = %d\n", Mon, num);
system("pause");
return 0;
}
三 .共用(联合)体
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以
联合也叫共用体)。
结合上面的代码 , 定义方式如下:
union Un un;
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
我们来看下面这段代码 :
#include<stdio.h>
#include<stdlib.h>
union Un
{
char c;
int i;
};
int main() {
union Un un;
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
printf("%d\n", sizeof(union Un));
printf("%d\n", offsetof(union Un, c));
printf("%d\n", offsetof(union Un, i));
system("pause");
return 0;
}
运行结果如下:
我们可以看到, union Un 类型所占内存字节数是4, 和较大的成员 i (int型)保持一致, un.i 和 un.c 的地址是一样的, 并且用宏offsetof(m, s)(了解宏offsetof(m, s)请点击 )计算出, 在union Un类型中, i 和 c 成员都是从共用体所开辟的内存空间的开头开始存储的, 共用了同一段内存空间 .
例如:
#include<stdio.h>
#include<stdlib.h>
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main() {
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
system("pause");
return 0;
}
运行如下 :
union Un1 中最大成员占4字节(最大对齐数为4字节), 字符数组c占5个字节, 比最大对齐数还大, 所以对齐到最大对齐数的整数倍 ,为8。
union Un2 中最大成员占4字节(最大对齐数为4字节), 短整型数组c占14个字节, 比最大对齐数还大, 所以对齐到最大对齐数的整数倍 ,为16。
关于自定义类型就写到这, 欢迎评论补充指正(●—●)
还没有评论,来说两句吧...