C语言:自定义类型详解

目录
  • 一、结构体
    • 1.结构体变量的定义及初始化
    • 2.结构体内存对齐
    • 3.为什么要内存对齐呢?
  • 二、位段
    • 1.什么是位段
    • 2.位段的内存分配
  • 三、枚举
    • 1.枚举的定义
    • 2.枚举的优点
  • 四、联合(共用体)
    • 1.联合类型的定义
    • 2.联合的特点
    • 3.联合大小的计算
  • 总结

一、结构体

1.结构体变量的定义及初始化

直接上代码:

struct Point {
	int x;
	int y;
}p1;     //创建结构体时顺便创建变量,分号一定不能掉
struct Point p2;    //单独创建变量
struct Point p3 = { 1,2 };   //创建变量时顺便赋值
struct Node {
	char str[20];
	struct Point p;     //结构体嵌套
}n1 = { "abcd",{3,4} };
int main() {
	printf("%s\n", n1.str);   //结构体访问时,用.或者->,变量访问用.,指针访问用->
	printf("%d\n", n1.p.x);
	printf("%d\n", n1.p.y);
	return 0;
}

struct是创建结构体的关键字,Point是结构体的名字,p1为结构体Point的一个变量,x,y称为结构体Point中的成员变量,变量的创建有两种形式,第一,可以在创建结构体时一起创建,第二,单独创建,创建规则为类型+名称,对于结构体的赋值,可以在创建变量的时候顺便赋值,可以先创建变量再单独赋值。结构体的访问有两种方式,当使用变量进行访问时,用.(点),再选择该变量对应的属性;当使用指针进行访问时,用->,再选择对应的属性即可。

2.结构体内存对齐

当我们想去计算结构体占内存大小的时候,就需要知道结构图内存对齐这一概念,我们先来看两个例子:

struct A {
	char a;
	char b;
	int c;
};
struct B {
	char a;
	int c;
	char b;
};
int main(){
	printf("%d\n", sizeof(struct A));
	printf("%d\n", sizeof(struct B));
	return 0;
}

结果表明,A和B两个结构体所占内存大小并不相等,但是其内部成员变量是一样的,只是顺序不一样。造成结果不同的原因就是因为内存对齐,我们来介绍结构体内存对齐的规则:

1.第一个成员在与结构体变量偏移量为0的地址处。

其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。在VS编译器中,默认对齐数是8。

结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

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

3.为什么要内存对齐呢?

从上面的结果我们大概就能猜到,为了节省空间。总的来说,主要原因有两点:

1.平台原因:

某些硬件平台只能在特定的地址处取地址,没有内存对齐,取值时可能会出错。

2.性能原因:

对于不对齐的情况,在读取数据时可能要读取两次,以下图为例:

在一个32位平台上,在不对齐的情况下,如果想要读取int的4个字节,第一次会读取到char的一个字节,和int的后3个字节(小端),需要再读取一次,才能将int的4个字节完全读取出来;相比较而言,如果是在内存对齐的情况下,只需要读取一次就可以把int的4个直接读取出来。

二、位段

结构体还有实现位段的能力,问题来了,什么是位段呢?

1.什么是位段

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

  • 位段的成员必须是 int、unsigned int 或signed int 。
  • 位段的成员名后边有一个冒号和一个数字。

例如:

struct A
{
 int _a:2;
 int _b:5;
 int _c:3;
 int _d:4;
};

A就是一个位段类型,想要知道A的大小,同样可以用sizeof来求。

2.位段的内存分配

拿上面的位段A来说,会先在内存中开辟一个4字节的空间,冒号后面的数字表示该成员变量所占内存的大小,单位为bit,位段中的成员在内存中从左向右分配, 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。总的来说,跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。光说不太好理解,我们来看下图:

当然,位段虽然比结构体更节省内存,但其存在跨平台问题,需要谨慎使用。

三、枚举

1.枚举的定义

enum Day//星期
{
 Mon,  //默认情况下Mon值为0,后面的成员变量的值依次递增
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};

当然也可以在定义的时候赋值,例如:

enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};

2.枚举的优点

我们知道,#define可以定义常量,那为什么要用枚举呢?

枚举的优点:

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

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

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

既然存在,就有其存在的道理,有些时候#define更方便,有时候枚举更方便,我们要学会合理使用

四、联合(共用体)

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;
// 下面输出的结果是一样的吗?
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);

从结果可以看出,i和c的地址是一样的,因为他们共用一块空间,当分别给i、c赋值时,后赋值的c会覆盖之前i的部分值。

3.联合大小的计算

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

例如:

union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];
 int i;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));  //8,c占5个字节,比i大,最大对齐数位4,需要为4的倍数,所以为8
printf("%d\n", sizeof(union Un2));  //16,c占14个字节,最大对齐数为4,所以为16

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言自定义军旗游戏源码

    本文实例为大家分享了C语言自定义军旗游戏的具体代码,供大家参考,具体内容如下 #include <graphics.h> #include <time.h> #define CHESIZE 40 // 棋盘尺寸,不能随意调整 #define RESETX 170 #define RESETY 350 // 重置原点 typedef enum // 要用到的棋子ID { si, jun, shi, lv, tuan, ying, lian, pai, ban, gong, fei,

  • C语言菜鸟基础教程之自定义函数

    先动手编写程序: #include <stdio.h> int add(int x, int y) { int z = x + y; return z; } int main() { int a = 1; int b = 2; int c = add(a, b); printf("c = %d\n", c); return 0; } 运行结果: c = 3 程序分析: (1) 函数定义的形式为: 类型 函数名称(类型 形式参数,--) { 函数体 } (2) 对应于咱们的程

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

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

  • C语言自定义函数的实现

    函数是一段可以重复使用的代码,用来独立地完成某个功能,它可以接收用户传递的数据,也可以不接收.接收用户数据的函数在定义时要指明参数,不接收用户数据的不需要指明,根据这一点可以将函数分为有参函数和无参函数. 将代码段封装成函数的过程叫做函数定义. C语言无参函数的定义 如果函数不接收用户传递的数据,那么定义时可以不带参数.如下所示: dataType functionName(){ //body } dataType 是返回值类型,它可以是C语言中的任意数据类型,例如 int.float.char

  • C语言自定义数据类型的结构体、枚举和联合详解

    结构体基础知识 首先结构体的出现是因为我们使用C语言的基本类型无法满足我们的需求,比如我们要描述一本书,就需要书名,作者,价格,出版社等等一系列的属性,无疑C语言的基本数据类型无法解决,所以就出现了最重要的自定义数据类型,结构体. 首先我们创建一个书的结构体类型来认识一下 struct Book { char name[20]; char author[20]; int price; }; 首先是struct是结构体关键字,用来告诉编译器你这里声明的是一个结构体类型而不是其他的东西,然后是Boo

  • 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语言中自定义类型详解

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

  • Go语言struct类型详解

    struct Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器.例如,我们可以创建一个自定义类型person代表一个人的实体.这个实体拥有属性:姓名和年龄.这样的类型我们称之struct.如下代码所示: 复制代码 代码如下: type person struct {     name string     age int } 看到了吗?声明一个struct如此简单,上面的类型包含有两个字段. 1.一个string类型的字段name,用来保存用户名称这个属性

  • Python的语言类型(详解)

    Python 是强类型的动态脚本语言 . 强类型:不允许不同类型相加 动态:不使用显示数据类型声明,且确定一个变量的类型是在第一次给它赋值的时候 脚本语言:一般也是解释型语言,运行代码只需要一个解释器,不需要编译 强类型语言和弱类型语言 1.强类型语言:使之强制数据类型定义的语言.没有强制类型转化前,不允许两种不同类型的变量相互操作.强类型定义语言是类型安全的语言,如Java.C# 和 python,比如Java中"int i = 0.0;"是无法通过编译的: 2.弱类型语言:数据类型

  • 易语言的输入字类型详解

    在程序中书写输入字时,可以使用一个半角符号来引导该输入字,以指定其类型.各输入字的类型引导符号为: 首拼及全拼输入字: 分号(";") 如: ;qz(1.23) 或 ;quzheng(1.23) 双拼输入字: 冒号(":") 如: :quvg(1.23) 英文输入字: 单引号(" '") 如: 'int(1.23) 系统具有一个当前默认输入法状态,如果某输入字前没有加上类型引导符号,则默认是属于该输入法的输入字.系统安装完毕后,当前默认输入法为&

  • django自定义非主键自增字段类型详解(auto increment field)

    1.django自定义字段类型,实现非主键字段的自增 # -*- encoding: utf-8 -*- from django.db.models.fields import Field, IntegerField from django.core import checks, exceptions from django.utils.translation import ugettext_lazy as _ class AutoIncreField(Field): description =

  • jsp中自定义Taglib详解

    一.自定义标签入门之无参数自定义标签 1.开发自定义标签类 当我们在JSP页面使用一个简单的标签时,底层实际上由标签处理类提供支持,从而可以使用简单的标签来封装复杂的功能,从而使团队更好地协作开发(能让美工人员更好地参与JSP页面的开发). 自定义标签类都必须继承一个父类:javax.servlet.jsp.tagext.SimpleTagSupport,或者TagSupport除此之外,JSP自定义标签类还有如下要求. 如果标签类包含属性,每个属性都有对应的getter和setter方法. 重

  • Javascript类型系统之String字符串类型详解

    javascript没有表示单个字符的字符型,只有字符串String类型,字符型相当于仅包含一个字符的字符串 字符串String是javascript基本数据类型,同时javascript也支持String对象,它是一个原始值的包装对象.在需要时,javascript会自动在原始形式和对象形式之间转换.本文将介绍字符串String原始类型及String包装对象 定义 字符串String类型是由引号括起来的一组由16位Unicode字符组成的字符序列 字符串类型常被用于表示文本数据,此时字符串中的

  • Java泛型映射不同的值类型详解及实例代码

    Java泛型映射不同的值类型详解 前言: 一般来说,开发人员偶尔会遇到这样的情形: 在一个特定容器中映射任意类型的值.然而Java 集合API只提供了参数化的容器.这限制了类型安全地使用HashMap,如单一的值类型.但如果想混合苹果和梨,该怎样做呢? 幸运的是,有一个简单的设计模式允许使用Java泛型映射不同的值类型,Joshua Bloch在其<Effective Java>(第二版,第29项)中将其描述为类型安全的异构容器(typesafe hetereogeneous Containe

随机推荐