自定义类型: 结构体 ,枚举, 共用(联合)体

自定义类型: 结构体 ,枚举, 共用(联合)体


目录

一. 结构体

     结构体的声明
     特殊的声明
     结构体的自引用
     结构体变量的定义和初始化 
     结构体传参
     结构体内存对齐
     修改默认对齐数
     位段

二. 枚举

枚举的优点 
枚举的定义
 枚举的使用

三 .联合(共用)体

共用(联合)体的声明
共用(联合)的定义
共用(联合)的特点
共同(联合)体的大小


一. 结构体

结构体(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 s;
};

这样是不行滴, 这样会造成自递归, 一个包含一个就无穷尽了, 所以编译器会报错 .

错误的看完就该看正确的了, 正确的自引用如下:

struct w{
	int a;
	char b;
	float c;
	struct w* next;
};
  • 结构体变量的定义和初始化 

struct w {
	int a;
	char b;
	float c;
}s1;
struct w s2;

 初始化 : 

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 定义常量,为什么非要使用枚举? 枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符相比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

  • 枚举的定义

enum Day{
	零,
	星期一,
	星期二,
	星期三,
	星期四,
	星期五,
	星期六,
	星期七
};

像结构体一样, 也可以没有标签 

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
{
	char c;
	int i;
};
  • 共用(联合)的定义

结合上面的代码 , 定义方式如下:

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。


关于自定义类型就写到这, 欢迎评论补充指正(●—●)

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值