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

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

一、结构体

结构体是不同类型变量的集合体

1.结构体的声明

struct Book
{
 char name[20];//名字
 int Price;//价格
 char Writer[5];//作者
 char Time[20];//日期
};    //注意分号不能丢

struct为结构体关键字,Book为结构体标签,中间不同类型的变量为结构体的成员。上述现在只是定义了一个结构体类型struct Book。

局部结构体变量

int main()
{
   struct Book B1; // B1为局部结构体变量
   return 0;
}

全局结构体变量

struct Book
{
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}B3,B4,B5;      //在结构体类型后可连续定义多个全局结构体变量

struct Book B2;   //B2为全局结构体变量

int main()
{
   return 0;
}

2.特殊声明

不完全声明

//匿名结构体类型--没有结构体标签
struct
{
 int a;
 char b;
 float c;
}x;
//这样的结构体类型必须紧跟着定义结构体变量
//后面不能定义变量

不完全声明类型只能在定义使用一次,并且在vs中:

struct
{
 int a;
 char b;
 float c;
}x;

struct
{
 int a;
 char b;
 float c;
}* ps;

int main()
{
  ps=&x;      //编译器默认两者类型不兼容  //且是错误写法
  return 0;
}

因此不完全声明很少使用,不推荐。 

3.结构体的自引用

这里可以用链表的实现来理解:

struct Node
{
 int data;
 struct Node* next;
};

这样就实现了自己类型的对象找自己类型对象的方法,这就是结构体的自引用。 

4.结构体变量的初始化

以上面struct Book为例:

struct Book
{
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}; 

int main()
{
   struct Book B1={"三脚猫",50,“阿里”,“20081001”};
   //初始化要用大括号
   return 0;
}

嵌套结构体的初始化:

struct Data
{
  int a;
  char b[6];
};

struct Book
{
 struct Data D;
 char name[20];
 int Price;
 char Writer[5];
 char Time[20];
}; 

int main()
{
   struct Book B1={{4,"haha"},"三脚猫",50,"阿里","20081001"};
   //大括号里加大括号
   return 0;
}

5.结构体内存对齐 

计算结构体在内存中的大小

方法:

1. 第一个成员为起始,设从下标为0的地址开始向后存储。

2. 其他成员变量要对齐到对齐数的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与 该成员大小的较小值。 VS中默认的值为8

3. 结构体总大小为所有成员对齐数中最大对齐数的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

举例说明:

#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

分析:

嵌套结构体

#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	struct S1 s1;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

分析:

内存对齐的存在,在平台和性能两方面,可以使访问空间更加高效,用空间换取时间。

让占用空间小的成员尽量集中在一起。

6.修改默认对齐数

#pragma pack(1)     //修改默认对齐数为1

//一般修改的对齐数为2^n

举例说明:

#include <stdio.h>
#pragma pack(1)   //修改为1相当于取消了对齐,没有优化,在实际应用中很少用
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S1));  // 6
	return 0;
}

当默认对齐数被修改后,每个类型的对齐数都变为1,整体的最大对齐数也为1(相当于没有对齐),整体大小是1的倍数,则1+4+1=6。

7.结构体传参

当一个函数涉及到结构体时,最好用传址调用:

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s);  //传结构体
 print2(&s); //传地址
 return 0;
}

传址调用原因:

1.函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

2.如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

二、位段

位段的声明和结构是类似的,有两个不同:

1.位段的成员必须是整型。

2.位段的成员名后边有一个冒号和一个数字。

举例:

struct S
{
 int _a:2;
 int _b:5;
 int _c:20;
 int _d:25;
};

此时S就是一个位段类型

他的大小为8

printf("%d\n", sizeof(struct S));

分析:

下面我们来分析位段在内存中的存储:

注:若初始化的值大于给其指定的空间,则先会发生截断(断左取右),再进行存储

位段是根据实际需求来进行开辟空间,目的是为了节省空间提高效率。

跨平台问题:

1. int 位段被当成有符号数还是无符号数是不确定的。

2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。(取决于编译器)

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

三、枚举

就是把可能的取值一一列举

枚举类型的定义:

例子:

enum Day  //星期
{
 Mon,
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};

enum Day就是一个枚举类型,{}中的内容是枚举类型的可能取值,也叫枚举常量。

枚举常量是有值的,默认从0开始,依次递增。

int main()
{
   enum Day d=Mon;   //定义一个变量,赋予{}内可能的取值。
   return 0;
}

在定义的时候也可以赋初值,后面的常量依次递增一

枚举的优点 

1. 增加代码的可读性和可维护性

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

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

4. 便于调试

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

注:最好用枚举常量给枚举变量赋值,才不会出现类型上的差异。

举例:

当用cpp来运行程序时会报错,因为cpp对代码的格式会更加严格

而c就可以运行过去

养成良好的代码风格,做到认真严谨。

四、联合

联合也是一种特殊的自定义类型

1.联合类型的定义

//联合类型的声明
union Un
{
 char c;
 int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un));

2.联合的特点 

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小。

union Un
{
	int i;
	char c;
};
union Un un;

int main()
{
	printf("%d\n", &(un.i));
	printf("%d\n", &(un.c));
	return 0;
}

共用一块空间,起始地址相同。

使用案例:

union Un
{
	int i;
	char c;
};
union Un un;

int main()
{
	//printf("%d\n", &(un.i));
	//printf("%d\n", &(un.c));
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}

分析:

3.联合大小的计算 

联合的大小至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

举例:

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));
	return 0;
}

分析:

到此这篇关于C语言 自定义类型全面理解的文章就介绍到这了,更多相关C语言 自定义类型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

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

  • 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语言自定义类型超详细梳理之结构体 枚举 联合体

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

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

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

  • C语言结构体,枚举,联合体详解

    目录 1.什么是结构体.枚举.联合体 2.定义结构体 2.1 包含结构体成员变量.variable 2.2 tag.结构体成员变量 2.3 用结构体声名变量 2.4 用typedef 创建新类型 2.5 两个结构体相互包含 2.6 结构体变量初始化 2.7 结构体指针 3.枚举 3.1 定义方式 3.2 为什么用枚举 3.3 枚举变量的定义 3.4 实例 3.5 枚举实际用途 4.联合体 4.1 与结构体区别 4.2 定义 总结 1.什么是结构体.枚举.联合体 结构体(struct)是由一系列具

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

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

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

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

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

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

  • 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语言深入探究自定义类型之结构体与枚举及联合

    目录 1.结构体 1.1结构体类型的声明 1.2结构的自引用 1.3结构体变量的定义和初始化 1.4结构体内存对齐 1.5结构体传参 1.6结构体实现位段(位段的填充&可移植性) 2.枚举 2.1枚举类型的定义 2.2枚举的优点 3.联合 3.1联合类型的定义 3.2联合的特点 3.3联合大小的计算 1.结构体 1.1结构体类型的声明 结构是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同类型的变量 这里给大家举个列子演示一下: //定义一个学生的结构体 typedef struct

  • go语言中值类型和指针类型的深入理解

    golang这个语言用起来和java. c#之类语言差不多,和c/c++差别比较大,有自动管理内存机制,省心省力. 然而,如果写golang真的按写java的习惯去写,也容易出问题,因为golang中有指针的概念,虽然这个指针是c/c++的自动化版本,但是却也有指针的特征,如果不熟悉其中原理,写出来的程序虽然不至于有运行BUG,性能却不友好. 因此,不能完全以写java的思路去写golang,一定要注意其中差别. 我们知道,在java之中,除了基本类型之外,所有的变量类型都是引用类型,你可以随意

随机推荐