C语言自定义类型全解析

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

前言

初学C语言

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

比如说int char short float double long等等

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

结构体类型

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

结构体就是各种值集合

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

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;
};

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

结构体的访问

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

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

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

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

(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;
}

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

成员内包含的数字代表的是这个成员需要利用的内存,单位是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;
}

位段的跨平台性

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;
}

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

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

枚举类型的优点

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和i存放在同一块内存空间上,修改c或者i都会影响到另一个成员。

 联合体内存大小的计算

<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语言自定义类型全解析的文章就介绍到这了,更多相关C语言 自定义类型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言:自定义类型详解

    目录 一.结构体 1.结构体变量的定义及初始化 2.结构体内存对齐 3.为什么要内存对齐呢? 二.位段 1.什么是位段 2.位段的内存分配 三.枚举 1.枚举的定义 2.枚举的优点 四.联合(共用体) 1.联合类型的定义 2.联合的特点 3.联合大小的计算 总结 一.结构体 1.结构体变量的定义及初始化 直接上代码: struct Point { int x; int y; }p1; //创建结构体时顺便创建变量,分号一定不能掉 struct Point p2; //单独创建变量 struct

  • C语言中自定义类型详解

    目录 结构大小 offsetof 结构体对齐规则 存在原因 总结 结构大小 我们先随便给出一个结构体,为了计算他的大小,我给出完整的打印方案: typedef struct num { char c; int n; char cc; }num; int main() { printf("%d\n", sizeof(num)); return 0; } 好了,按道理来说我计算一个结构体大小就看他的各个成员需要消耗多大的空间, num 结构体中三个成员分别是 char ,int ,char

  • C语言自定义类型的保姆级讲解

    前言 在我们日常写代码时,经常会遇到结构体类型的使用,今天带读者了解结构体类型的使用. 一.初始结构体 在了解结构体之前,我们先来了解一下结构体的基础只是,结构体到底是什么? 结构是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同类型的变量. 下面举一个例子: struct tag { menber_list; //成员列表 }variable_list; //变量列表 例如我们使用结构体描述一台电脑 struct computer { int price;//价格 char name

  • C语言自定义类型详解(结构体、枚举、联合体和位段)

    目录 前言 一.结构体 1.结构体类型的声明 2.结构体的自引用 3.结构体变量的定义和初始化 4.结构体内存对齐 5.结构体传参 二.位段 1.位段的定义 2.位段的内存分配 3.位段的应用 三.枚举 1.枚举类型的定义 2.枚举的优点 3.枚举的使用 四.联合体(共用体) 1.联合体的定义 2.联合体的特点 3.联合体的大小计算 总结 前言 一.结构体 1.结构体类型的声明 当我们想要描述一个复杂变量--学生,可以这样声明. ✒️代码展示: struct Stu { char name[20

  • C语言自定义类型全解析

    目录 前言 结构体类型 结构体的声明 结构体变量的定义与初始化 结构体的自引用 结构体的访问 结构体的传参 传结构体 传地址 结构体的内存对齐(强烈建议观看) 位段 位段的声明 位段的内存管理 位段的跨平台性  枚举类型 枚举类型的定义 枚举类型赋予初始值 枚举类型的优点 联合体类型 联合体的定义 联合体的特点  联合体内存大小的计算 前言 初学C语言 我们先接触的都是内置的类型 比如说int char short float double long等等 这一期就来聊一聊自定义类型的知识 结构体

  • MySQL INT类型全解析

    前言: 整型是MySQL中最常用的字段类型之一,通常用于存储整数,其中int是整型中最常用的,对于int类型你是否真正了解呢?本文会带你熟悉int类型相关知识,也会介绍其他整型字段的使用. 1.整型分类及存储范围 整数类型 字节 有符号范围 无符号范围 TINYINT 1 -128 ~ 127 0 ~ 255 SMALLINT 2 -32768 ~ 32767 0 ~ 65535 MEDIUMINT 3 -8388608 ~ 8388607 0 ~ 16777215 INT/INTEGER 4

  • C语言自定义类型超详细梳理之结构体 枚举 联合体

    目录 一.什么是结构体 1.结构体实现 2.匿名结构体类型 3.结构体自引用 4.结构体的内存对齐 5.结构体位段  二.什么是枚举 1.枚举类型的定义 2.枚举的优点 三.联合(共用体) 1.什么是联合(共用体) 2.联合(共用体)的定义 3.联合(共用体)的初始化 总结 一.什么是结构体 结构是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同类型的变量. //结构体声明 struct tag //struct:结构体关键字,tag:标签名,合起来是结构体类型(类型名) { memb

  • C语言 自定义类型全面系统理解

    目录 一.结构体 1.结构体的声明 局部结构体变量 全局结构体变量 2.特殊声明 3.结构体的自引用 4.结构体变量的初始化 5.结构体内存对齐  6.修改默认对齐数 7.结构体传参 传址调用原因: 二.位段 举例: 分析: 跨平台问题: 三.枚举 枚举类型的定义: 枚举的优点  四.联合 1.联合类型的定义 2.联合的特点  使用案例: 分析: 3.联合大小的计算  举例: 分析: 一.结构体 结构体是不同类型变量的集合体 1.结构体的声明 struct Book { char name[20

  • C语言中的自定义类型之结构体与枚举和联合详解

    目录 1.结构体 1.1结构的基础知识 1.2结构的声明 1.3特殊的声明 1.4结构的自引用 1.5结构体变量的定义和初始化 1.6结构体内存对齐 1.7修改默认对齐数 1.8结构体传参 2.位段 2.1什么是位段 2.2位段的内存分配 2.3位段的跨平台问题 2.4位段的应用 3.枚举 3.1枚举类型的定义 3.2枚举的优点 3.3枚举的使用 4.联合 4.1联合类型的定义 4.2联合的特点 4.3联合大小的计算 1.结构体 1.1结构的基础知识 结构是一些值的集合,这些值称为成员变量.结构

  • C#语言基础——结构体和枚举类型全面解析

    一.结构体(struct) 结构类型是用户自己定义的一种类型,它是由其他类型组合而成的,可包含构造函数.常数.字段.方法.属性.索引器.运算符.事件和嵌套类型的值类型.结构在几个重要方面不同于类:结构为值类型而不是引用类型,并且结构不支持继承. 用结构的主要思想是用于创建小型的对象,如Point和FileInfo等等.这可以节省内存,因为没有如类对象所需的那样有额外的引用产生.例如,当声明含有成千上万个对象的数组时,这会引起极大的差异. 结构体是一个变量组,将一组变量放在一起,其实就是一个自定义

随机推荐