目录
  • 前言
  • 结构体类型
    • 结构体的声明
    • 结构体变量的定义与初始化
    • 结构体的自引用
    • 结构体的访问
    • 结构体的传参
      • 传结构体
      • 传地址
    • 结构体的内存对齐(强烈建议观看)
    • 位段
      • 位段的声明
        • 位段的内存管理
          • 位段的跨平台性
          •  枚举类型
            • 枚举类型的定义
              • 枚举类型赋予初始值
                • 枚举类型的优点
                • 联合体类型
                  • 联合体的定义
                    • 联合体的特点
                    •  联合体内存大小的计算

                      前言

                      初学C语言

                      我们先接触的都是内置的类型

                      比如说int char short float double long等等

                      这一期就来聊一聊自定义类型的知识

                      C语言自定义类型全解析

                      结构体类型

                      首先我们要知道什么是结构体

                      结构体就是各种值集合

                      这些值被称作结构体成员,这些成员可包括各种不同的类型

                      struct tag         //这里的struct是结构体的关键字,tag是结构体标签,也就是结构体的名称
                      {
                      	number - list; //结构体成员列表
                      }veriable-list;    //结构体的变量 

                      结构体的声明

                      如果结构体的标签是student,我拿student来举例子

                      结构体的完整声明

                      struct Student
                      {
                          char name[20];//姓名
                          char sex;//性别
                          int age;//年龄
                          int num;//学号
                      };   //这里的分号不能丢

                      结构体的不完全声明(匿名结构体类型)

                      struct
                      {
                      	int a;
                      	char b;
                      	double c;
                      }s;   //这s不能省略

                      匿名结构体的特点就是没有结构体标签

                      但这样写用户使用时只能使用一次,也就是说只能在结构体声明时就定义变量

                      因为你找不到结构体标签,就相当于找不到门牌号一样,无法再对其定义一个变量

                      结构体变量的定义与初始化

                      结构体的定义大致分为三种情况

                      <1>结构体声明的同时定义

                      struct Student 
                      {
                          char name[20];//姓名
                          char sex[20];//性别
                          int age;//年龄
                      }s={"zhangsan","nan",20};

                      <2>结构体先声明,再定义

                      #include<stdio.h>
                      struct Student
                      {
                          char name[20];//姓名
                          char sex;//性别
                          int age;//年龄
                          int num;//学号
                      };
                       
                       
                      int main()
                      {
                          struct Student s = { "zhangsan",'w',20,111 };
                      	return 0;
                      }

                      <3>匿名结构体定义

                      struct 
                      {
                          char name[20];//姓名
                          char sex[20];//性别
                          int age;//年龄
                      } s = { "zhangsan","nan",20};

                      注意:结构体初始化与数组相同,都必须整体进行赋值。

                      结构体的自引用

                      struct Node //初始话链表
                      {
                      	int a;
                      	struct Node next;
                      };

                      结构体的自引用就是结构体再套用自己

                      学过数据结构的朋友应该知道这是初始化链表

                      不过这一个代码有问题的

                      问题在于无法求出这个结构体的大小,不清楚这个结构体有多大,因为无法求出自引用的结构体有多大

                      所有自引用的结构体要用指针来访问

                      struct Node //初始话链表
                      {
                      	int a;
                      	struct Node* next;
                      };

                      故就可以通过指针来访问每一个结点

                      C语言自定义类型全解析

                      结构体的访问

                      当结构体定义且变量初始化完成后,就可以通过操作符来访问变量中的成员

                      当然,这里给出了两个操作符

                      分别是  .操作符和 -> 操作符

                      当使用结构体变量时,就用点操作符,当访问结构体指针变量就用箭头操作符

                      (1)通过结构体变量进行访问:

                      printf("%s\n",s.name);

                      (2)通过结构体指针进行访问:

                      printf("%s\n",ps->name);

                      结构体的传参

                      函数的调用有时候需要传一些参数

                      参数的类型可以是不同的类型,可以是数组,也可以是指针

                      同样结构体的传参也可通过传结构体或者传指针

                      传结构体

                      #include<stdio.h>  
                      struct tag
                      {
                      	int a;
                      	char b[20];
                      }s1 = { 100,"abcdef" };
                      void print()
                      {
                      	printf("%d", s1.a);
                      }
                      int main()
                      {
                      	print(s1);
                      	return 0;
                      }

                      传地址

                      #include<stdio.h>
                      struct tag
                      {
                      	int a;
                      	char b[20];
                      }s2 = { 100,"abcdef" };
                      void print(struct tag*s2)
                      {
                      	printf("%d", s2->a);
                      }
                      int main()
                      {
                      	print(&s2);
                      	return 0;
                      }

                      我们要知道函数传参是形参就是实参的临时拷贝

                      参数是要压栈的(向系统申请空间),既然是临时拷贝,就会再次再栈上开辟空间,当实参足够大时,显然会浪费一定的空间和时间

                      相比较与传结构体,传指针会更好 

                      结构体的内存对齐(强烈建议观看)

                      在另外一篇文章详细讲过——C语言结构体中内存对齐的问题理解

                      位段

                      可能有人没有听过什么是位段

                      位段的结构类型跟结构体有些类似可以类似结构体去学习

                      也可以说

                      位段是结构体特殊的实现

                      位段的声明

                      相较于结构体,位段的声明有两点不同

                      <1>规定位段的成员的必须是int,unsigned int ,signed int (但是写成char类型也没什么大的问题)

                      <2>位段的成员后面有一个冒号和一个数字

                      struct A  //位段的声明
                      {
                      	int _a : 2;
                      	int _b : 5;
                      	int _c : 10;
                      	int _d : 30;
                      };

                      位段的内存管理

                      #include<stdio.h>
                      struct A
                      {
                      	int a : 2;
                      	int b : 5;
                      	int c : 10;
                      	int d : 30;
                      };
                      int main()
                      {
                      	
                      	printf("大小是%d字节", sizeof(struct A));
                      	return 0;
                      }

                      C语言自定义类型全解析

                      为什么位段的大小是八个字节呢?

                      成员内包含的数字代表的是这个成员需要利用的内存,单位是bit。

                      位段成员申请内存时都是以四个字节或者一个字节单位(当成员是char类型时)

                      int a : 2; //申请4个字节,也就是32个bit位,利用两个还剩30个
                      int b : 5; //利用5个,还剩25个
                      int c : 10; //利用10个,还剩15个
                      int d : 30; //这里的十五不够,所以再申请了4个字节

                      最终的结果就是八字节

                      但问题是,变量d利用的空间是留下的15个bit加上重新申请的空间呢

                      这个结果在不同的环境的结果是不同的,所以位段的跨平台性比较差

                      位段使用的前提是你知道存储的内存大概有多大

                      就比如说年龄

                      十个bit位0111111111,最大值就可以达到1023

                      就不需要再申请一次利用一个int类型的空间大小

                      这就达到了节省内存的作用,存在即合理

                      位段的应用 

                      struct A
                      {
                      	char a : 3;
                      	char b : 4;
                      	char c : 5;
                      };
                      main()
                      {
                      	struct A s = { 0 };
                      	s.a = 10;
                      	s.b = 12;
                      	s.c = 3;
                      	return 0;
                      }

                      C语言自定义类型全解析

                      C语言自定义类型全解析

                      位段的跨平台性

                      1.int位段被当成有符号数还是无符号数是不确定的,有时候系统会自动转化为无符号整形。

                      2.位段中最大位的数目不能确定。(因为在早期的16位机器int最大16,而32位机器int最大32)

                      3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

                      意思当内存一个字节00000000,存入01010,可能会出现00001010或者01010000

                      4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。

                       枚举类型

                      枚举类型适用于可以一一列举的类型

                      比如说星期、性别

                      枚举类型的定义

                      enum Day   //星期
                      {
                      	//枚举的可能取值
                      	Mon,
                      	Tues,
                      	Wed,
                      	Thir,
                      	Fri,
                      	Sta,
                      	Sun
                      };
                      enum Sex  //性别
                      {
                      	MALE,//0
                      	FEMALE,//1
                      	SECRET//2
                      };

                      枚举类型是有初始值的

                      如果你没赋予它初始值,就会默认从零开始,依次加一

                      枚举类型赋予初始值

                      #include<stdio.h>
                      enum Sex  //性别
                      {
                      	MALE = 4,
                      	FEMALE=10,
                      	SECRET//
                      };
                      main()
                      {
                      	printf("%d %d %d", MALE,FEMALE,SECRET);
                      	return 0;
                      }

                      C语言自定义类型全解析

                      可以看到,其实默认的值是可以改的

                      当某个成员被赋予某个值的时候,后续的成员就在此基础上加一

                      枚举类型的优点

                      1.相较于数字,枚举增加代码的可读性和可维护性

                      2.和#define定义的标识符比较枚举有类型检查,更加严谨。

                      3.防止了命名污染(封装)

                      4.便于调试

                      5.使用方便,一次可以定义多个常量

                      联合体类型

                      联合体类型也叫共用体

                      联合体的定义

                      union Un  
                      {
                      	int a;
                      	char b;
                      };

                      union是联合体关键字,Un是联合体的标签

                      联合体的特点

                      共用体,顾名思义,这里的共用就是公用内存

                      内存的也可被折叠

                      #include<stdio.h>
                      union Un
                      {
                      	char c;
                      	int i;
                      };
                      int main()
                      {
                      	union Un u = {0};
                      	printf("%d\n", sizeof(u));
                      	printf("%p\n", &u);
                      	printf("%p\n", &(u.c));
                      	printf("%p\n", &(u.i));
                      	return 0;
                      }

                      C语言自定义类型全解析

                      他们有相同的地址

                      c和i存放在同一块内存空间上,修改c或者i都会影响到另一个成员。

                      C语言自定义类型全解析

                       联合体内存大小的计算

                      <1>联合体内存大小是最大成员的大小

                      <2>最大成员的大小如果不是最大对齐数的整数倍,就会对齐到最大对齐数的整数倍

                      (联合体也存在内存对齐)

                      #include<stdio.h>
                      union Un1
                      {
                      	char c[5];  
                      	int i;
                      };          
                      //Un1成员最大成员大小5,最大对齐数是4,所以Un1的大小是8;
                      union Un2
                      {
                      	char c[7];
                      		int i;
                      };
                      //Un2成员最大成员大小7,最大对齐数是4,所以Un2的大小是8;
                      int main()
                      {
                      	printf("%d\n", sizeof(union Un1));
                      	printf("%d\n", sizeof(union Un2));
                      	return 0;
                      }

                      C语言自定义类型全解析

                      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。