C语言 struct结构体超详细讲解

目录
  • 一、本章重点
  • 二、创建结构体
  • 三、typedef与结构体的渊源
  • 四、匿名结构体
  • 五、结构体大小
  • 六、结构体指针
  • 七、其他

一、本章重点

  • 创建结构体
  • typedef与结构体的渊源
  • 匿名结构体
  • 结构体大小
  • 结构体指针
  • 其他

二、创建结构体

先来个简单的结构体创建

这就是一个比较标准的结构体

struct people
{
	int age;
	int id;
	char address[10];
	char sex[5];
};//不要少了分号。

需要注意的是不要少了分号。

那么这样创建结构体呢?

struct phone
{
	char brand[10];//品牌
	int price;//价格
};

struct people
{
	int age;
	int id;
	char address[10];
	char sex[5];
	struct phone;
};

很显然,一个结构体是能够嵌套另一个结构体的。

没有这样的设计,这样做也行

struct people
{
	int age;
	int id;
	char address[10];
	char sex[5];
	char phone_brand[10];
	int phone_price;
};

但结构体中成员太多了是不利于我们后期的维护的,试问:假设有1000个成员,你能快速的找出你需要的成员吗?当有了分块的结构体,我们是能够迅速的定位和查看的。

结构体能够嵌套另一结构体,那么结构体能否嵌套自己呢?

struct phone
{
	char brand[10];
	int price;
	struct phone;
};

这样做之后编译器会给你一个报错

原因是什么呢?

因为这个结构体的大小是未定义的,你能算出这个结构体的大小吗?这是不可能的!

既然大小不能确定,那么当你用这个结构体去创建变量,编译器该为这个变量开辟多大的空间呢?所有编译器在设计之初便杜绝了这种可能。

在提一个问题,结构体是否可以嵌套自己的结构体指针呢?

struct people
{
	int age;
	int id;
	char address[10];
	char sex[5];
	struct people* son[2];
};

答案是:可以

这里并不存在空间该分配多少的问题,因为struct people*是指针类型,它的大小是确定的,在32位机器下是4字节,64为机器是8字节。

三、typedef与结构体的渊源

先上一段代码

struct people
{
	int age;
	int id;
}a;//a代表什么?

int main()
{
	a.age = 20;
	printf("%d\n", a.age);
	return 0;
}

提问:a代表什么?

其实我们可以这样去看这个问题

struct people{int age;int id;} a;

int main()
{
	a.age = 20;
	printf("%d\n", a.age);
	return 0;
}

对比int b呢?

int b;
struct people{int age;int id;} a;

int main()
{
	a.age = 20;
	printf("%d\n", a.age);
	return 0;
}

显然,struct people{int age;int id;}代表的是结构体类型,就像整形类型一样去创建变量。

那么这里的a就是结构体创建的变量。

这里也能明白结构体创建的最后为什么要保留分号。

那我们再看一段代码:

typedef struct people
{
	int age;
	int id;
}a;

int main()
{
	a.age = 20;
	printf("%d\n", a.age);
	return 0;
}

此时加上typedef,a还能当结构体创建的变量吗?

显然不行,此时编译器会报错。

理解方式以上述一致。

typedef struct people{int age;int id;} a;

类似于

typedef struct people{int age;int id;} a;
typedef int b;

此时的a就是struct people{int age;int id;}

typedef的作用是把struct people{int age;int id;}这一类型重命名为a。

不知道你有没有见过这样的代码

typedef struct people
{
	int age;
	int id;
}b,a,*c;

int main()
{
	a a1;
	b b1;
	c c1 = &a1;
	a1.age = 5;
	b1.age = 6;
	c1->age = 10;
	printf("%d %d %d\n", a1.age, b1.age, c1->age);
	return 0;
}

你知道运行结果吗?

这里的b、a、c是什么呢?

这里我就不啰嗦了,a和b都是struct people{int age;int id;}结构体类型,c是struct people{int age;int id;}*  结构体指针类型。

运行结果:

那么再次提升一下

typedef struct people
{
	int age;
	int id;
}b, a, c[20];
这里的b、a、c代表什么呢?

期待着你动手解决这一问题。

四、匿名结构体

这就是一个匿名的结构体

struct
{
	int age;
	int id;
};

int main()
{
	struct p1;
	p1.age = 10;
	printf("%d\n", p1.age);
	return 0;
}

匿名结构体能这样创建结构体变量吗?

此时编译器会报错

这样的匿名结构体只能在创建结构体的时候定义好变量。

比如这样

struct
{
	int age;
	int id;
}p1;

int main()
{
	p1.age = 10;
	printf("%d\n", p1.age);
	return 0;
}

接下来我们看下这段代码

typedef struct
{
	int age;
	int id;
}people;

int main()
{
	people p1;
	p1.age = 10;
	printf("%d\n", p1.age);
	return 0;
}

这里我们重命名这个匿名结构体,即把这个结构体类型重命名为people。

那么我们自然可以用people类型来创建p1。也可创建p2、p3等等。

运行结果:

以下代码合法吗?

//匿名结构体类型
struct
{
 int a;
 char b;
 float c;
}x;

struct
{
 int a;
 char b;
 float c;
}a[20], *p;

int main()
{
    //在上面代码的基础上,下面的代码合法吗?
    p = &x;
    return 0;
}

警告: 编译器会把上面的两个声明当成完全不同的两个类型。 所以是非法的。

五、结构体大小

如何求结构体类型的大小?

这需要了解结构体成员在内存是怎么存储的。

你知道下面这段代码的运行结果吗?

struct people
{
	char a;
	int b;
	char c;
};

int main()
{
	struct people p1;
	printf("%d\n", sizeof(p1));//大小是6吗?
	return 0;
}

char是一字节大小

int是四字节大小

char是一字节大小

直接相加等于6

那么这个结构体的大小是6吗?

但我们发现答案是12

这是为什么呢?

简单来说编译器为了读取内存时提升效率和避免读取出错,它做了内存对齐的操作。

什么是内存对齐?

就是让数据安排在合适的位置上所进行的对齐操作。

为什么要内存对齐?

  • 一、移植原因

1.不是所有的硬件平台都能访问任意地址上的任意数据的;

2.某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  • 二、性能原因:

为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

对齐规则:

Windows中默认对齐数为8,Linux中默认对齐数为4

  • 一:第一个数据成员从偏移地址为0的地方开始存放。
  • 二:对齐数:等于默认对齐数与与该成员大小的较小值。
  • 三:从第二成员开始,从它对齐数的整数倍的偏移地址开始存放。
  • 四:最后结构体的大小需要调整为最大对齐数的整数倍。

最大对齐数:即所有成员对齐数中最大的对齐数。

struct people
{
	char a;
	int b;
	char c;
};

解析:

第一个为char,直接放在偏移地址为0的位置处。

第二个为int,它的自身大小为4,比默认对齐数小,所以它的对齐数是4。

4是4的整数倍,所以在偏移地址为4处开始放数据。

第三个为char,自身大小为1,比默认对齐数小,它的对齐数是1。

8是1的整数倍,从偏移地址为8的位置开始放。

总大小为9,不是最大对齐数的整数倍

要浪费3个空间,调整后为12。

我们再看看下面这段代码:

将char 和 int成员变量交换位置后,这结构体的大小还是12吗?

struct people
{
	char a;
	char c;
	int b;
};

int main()
{
	struct people p1;
	printf("%d\n", sizeof(p1));//大小是多少呢?
	return 0;
}

解析:

第一个:直接从0处开始放

第二个是char:对齐数是1,1是1的整数倍,从偏移地址为1的位置开始放。

第二个是int:对齐数是4,4是4的整数倍,从偏移地址为4的位置开始放。

放好各个成员变量后,总大小是8,是最大对齐数(4)的整数倍,不需要调整。

因此这个结构体的大小是8.

再来看看下面这个如何?

struct people
{
	char a;
	int b;
	short c[2];
	char d;
};

int main()
{
	struct people p1;
	printf("%d\n", sizeof(p1));//大小是6吗?
	return 0;
}

解析:

第一个成员是char,直接放再0地址处。

第二个成员是int,对齐数是4,从偏移地址为4的位置处开始存放。

第三个成员是short[2],关于数组,它的对齐数是首元素的大小与默认对齐数的较小值,这里它的对齐数是2,然后从偏移地址为8处开始存放4个字节。

第四个成员是char,对齐数是1,直接放开12位置处。

总大小是13,最大对齐数是4,不是最大对齐数的整数倍,需要对齐到最大对齐数的整数倍,浪费3字节大小,对齐到16.

所以这个结构体的大小是16.

六、结构体指针

先创建一个结构体

struct people
{
	char a;
	int b;
};

然后用该结构体创建变量,再用结构体指针指向该变量。

int main()
{
	struct people p1;
	struct people* p = &p1;
	return 0;
}

所谓结构体指针,即指向结构体的指针。

正如整形指针,即指向整形的指针。

访问变量方式1:

int main()
{
	struct people p1;
	struct people* p = &p1;
	p1.a = 'a';
	p1.b = 10;
	printf("%c %d\n", p1.a, p1.b);
	return 0;
}

访问变量方式2:

int main()
{
	struct people p1;
	struct people* p = &p1;
	p->a = 'a';
	p->b = 10;
	printf("%c %d\n", p->a, p->b);
	return 0;
}

访问变量方式3:

int main()
{
	struct people p1;
	struct people* p = &p1;
	(*p).a = 'a';
	(*p).b = 10;
	printf("%c %d\n", (*p).a, (*p).b);
	return 0;
}

七、其他

结构体中还有两个常见知识点:

一、位端

二、柔性数组

由于篇幅原因,下期会细细讲解这两个知识点

到此这篇关于C语言 struct关键字超详细讲解的文章就介绍到这了,更多相关C语言 struct内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言之结构体(struct)详解

    目录 为什么需要引入结构体 struct 定义 typedef与#define 结构体变量初始化及成员访问 结构体访问 总结 为什么需要引入结构体 原有的数据类型不能满足需求,因此才设计了构造类型结构体 struct 定义 struct name { 基础类型 结构成员 } 1.无名构造类型 2.有名构造类型 struct student { char name[30]; char sex; int age; float high; }stu; struct student stu2; 3.别名

  • C语言中结构体(struct)的几种初始化方法

    本文给大家总结的struct数据有3种初始化方法 1.顺序 2.C风格的乱序 3.C++风格的乱序 下面通过示例代码详细介绍这三种初始化方法. 1)顺序 这种方法很常见,在一般的介绍C的书中都有介绍.顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化:允许只初始化部分成员:在被初始化的成员之前,不能有未初始化的成员. 示例: struct User oneUser = {10, "Lucy", "/home/Lucy"}; 2)乱序(C风格) 顺序的缺陷是

  • C语言变长数组 struct中char data[0]的用法详解

    今天在看一段代码时出现了用结构体实现变长数组的写法,一开始因为忘记了这种技术,所以老觉得作者的源码有误,最后经过我深思之后,终于想起以前看过的用struct实现变长数组的技术.下面是我在网上找到的一篇讲解很清楚的文章. 在实际的编程中,我们经常需要使用变长数组,但是C语言并不支持变长的数组.此时,我们可以使用结构体的方法实现C语言变长数组. struct MyData { int nLen; char data[0];}; 在结构中,data是一个数组名:但该数组没有元素:该数组的真实地址紧随结

  • C语言 结构体(Struct)详解及示例代码

    前面的教程中我们讲解了数组(Array),它是一组具有相同类型的数据的集合.但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放. 在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为: struct 结构体名{     结构体所包含的变量或数组 }; 结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可

  • C语言中结构体struct编写的一些要点解析

    一.关于结构体的声明 1.匿名声明.如: struct { int i,j; }point; 说明: 这段代码的含义是,声明一个无名(anonymous)的结构体,并创建了一个结构体变量point.如果这段声明是放在全局域(在任意函数(比如main函数)外)内,那么point内的变量将被初始化为默认值,换句话说,以这种方式声明结构体变量时就已经为它分配了内存空间. 适用于该结构体只需要产生一个变量!本例中,该匿名结构体将有且仅有point这个结构体变量! 不同的匿名结构体变量,类型是不同的!如

  • C语言结构体(struct)的详细讲解

    目录 引言 1. 动态内存管理 2. 结构体 2.1 定义语法 2.2 定义示例 2.3 初始化 2.4 结构体赋值 2.5 结构体数组 2.6 结构体指针赋值 3. 学生管理系统 附:结构体变量的存储原理 总结 引言 当前文章介绍动态堆空间内存分配与释放,C语言结构体定义.初始化.赋值.结构体数组.结构体指针的相关知识点,最后通过一个学生管理系统综合练习结构体数组的使用. 1. 动态内存管理 C语言代码----->编译----->链接------>可执行的二进制文件(windows下x

  • C语言结构体(struct)常见使用方法(细节问题)

    基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量. 结构体定义: 第一种:只有结构体定义 struct stuff{ char job[20]; int age; float height; }; 第二种:附加该结构体类型的"结构体变量"的初始化的结构体定义 //直接带变量名Huqinwei struct stuff{ char job[20]; int age; floa

  • C语言 struct结构体超详细讲解

    目录 一.本章重点 二.创建结构体 三.typedef与结构体的渊源 四.匿名结构体 五.结构体大小 六.结构体指针 七.其他 一.本章重点 创建结构体 typedef与结构体的渊源 匿名结构体 结构体大小 结构体指针 其他 二.创建结构体 先来个简单的结构体创建 这就是一个比较标准的结构体 struct people { int age; int id; char address[10]; char sex[5]; };//不要少了分号. 需要注意的是不要少了分号. 那么这样创建结构体呢? s

  • C语言结构体超详细讲解

    目录 前言 1.结构体的声明 1.1 结构的基础知识 1.2 结构的声明 1.3 结构成员的类型 1.4 结构体变量的定义和初始化 2.结构体成员的访问 2.1 点操作符访问 2.2 ->操作符访问 3.结构体传参 3.1 参数是结构体类型的变量 3.2 参数是结构体类型的变量的地址 3.3 结构体传参对比 总结 前言 本文开始学习结构体的知识点,主要内容包括: 结构体类型的声明 结构体初始化 结构体成员访问 结构体传参 1.结构体的声明 1.1 结构的基础知识 结构是一些值的集合,这些值称为成

  • C语言数据的存储超详细讲解上篇

    目录 前言 1.数据类型介绍 类型的基本归类 2.整形在内存中的存储 2.1 原码.反码.补码 2.2 大小端介绍 2.2.1 什么是大小端 2.2.2 大端和小端意义 2.2.3 写程序判断字节序 总结 前言 本文开始学习C语言进阶的内容了,进阶内容,是在基础阶段的内容上进行拓展,有的知识点,在基础阶段也已经学过.在进阶内容中,将从更深层次的角度去理解学习,本文主要内容包括: 数据类型详细介绍 整形在内存中的存储:原码.反码.补码 大小端字节序介绍及判断 浮点型在内存中的存储解析 1.数据类型

  • C语言数据的存储超详细讲解下篇浮点型在内存中的存取

    目录 前言 浮点型在内存中的存储 浮点数存储的例子 浮点数存储规则 IEEE 754规定 IEEE 754对有效数字M的特别规定 IEEE 754对指数E的特别规定 存入内存是E的规定 从内存取出时E的规定 举例 1 举例 2 举例 3 判断两个浮点数是否相等? 总结 前言 本文接着学习数据的存储相关的内容,主要学习浮点型数在内存中的存储与取出. 浮点型在内存中的存储 常见的浮点数:3.14159.1E10 浮点数家族包括: float.double.long double 类型 浮点数表示的范

  • C语言数据的存储超详细讲解中篇练习

    目录 前言 数据的存储的知识点练习 练习 1 练习 2 练习 3 练习 4 练习 5 练习 6 练习 7 总结 前言 本文继续学习数据在内存中存储的相关知识点. 数据存储 整型提升 数据的存储的知识点练习 通过几个练习来深入学习数据在内存中存储的知识点,先复习前面学过的整形提升的知识点:C语言操作符超详细讲解下篇 整形提升是按照变量的数据类型的符号来提升的 负数的整形提升,最高位补充符号位,即1,例如 char a=-1;11111111 截断后的补码1111111111111111111111

  • C语言struct结构体介绍

    目录 struct struct的嵌套 实验 struct C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能. 下面是struct自定义数据类型的一个例子. struct tag { member-list member-list member-list ... } variable-list; 声明了数据类型car和该类型的变量car. struct car { char *name; float price; int spe

  • C语言超详细讲解结构体与联合体的使用

    目录 结构体 offsetof-宏 位段 枚举 联合体(共用体) 结构体 结构体内存对齐问题: 当我们在计算结构体的大小时,我们便需要清楚的知道结构体内存对齐是什么. 存在内存对齐的原因可细分为两个: 平台原因: 不是所有的硬件平台都能方位任意地址上的任意数据:某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常. 性能原因: 首先内存对齐可以提高程序的性能,当访问未对其的内存空间时,有时候处理器需要进行两次访问,而当访问对齐的内存时,只需要一次就够了.这同时也被叫做 用空间换取

  • C语言数据结构超详细讲解单向链表

    目录 1.链表概况 1.1 链表的概念及结构 1.2 链表的分类 2. 单向链表的实现 2.1 SList.h(头文件的汇总,函数的声明) 2.2 SList.c(函数的具体实现逻辑) 2.2.1 打印链表 2.2.2 搞出一个新节点(为其他函数服务) 2.2.3 链表尾插 2.2.4 链表头插 2.2.5 链表尾删 2.2.6 链表头删 2.2.7 查找节点 2.2.8 在pos位置之前插入 2.2.9 在pos位置之后插入 2.2.10 删除pos位置 2.2.11 删除pos之后位置 2.

  • C语言超详细讲解数据结构中双向带头循环链表

    目录 一.概念 二.必备工作 2.1.创建双向链表结构 2.2.初始化链表 2.3.动态申请节点 2.4.打印链表 2.5.销毁链表 三.主要功能 3.1.在pos节点前插入数据 尾插 头插 3.2.删除pos处节点数据 尾删 头删 3.3.查找数据 四.总代码 List.h 文件 List.c 文件 Test.c 文件 五.拓展 一.概念 前文我们已经学习了单向链表,并通过oj题目深入了解了带头节点的链表以及带环链表,来画张图总体回顾下: 在我们学习的链表中,其实总共有8种,都是单双向和带不带

  • C语言超详细讲解队列的实现及代码

    目录 前言 队列的概念 队列的结构 队列的应用场景 队列的实现 创建队列结构 队列初始化 队列销毁 入队列 出队列 队列判空 获取队列元素个数 获取队列头部元素 获取队列尾部元素 总代码 Queue.h 文件 Queue.c 文件 Test.c 文件 前言 队列的概念 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头 队列和前文所学的栈

随机推荐