C的自定义类型

目录

1. 结构体

1.1. 结构体类型的声明

1.1.1. 特殊声明

2. 结构的自引用

3. 结构体变量的定义和初始化

4. 结构体内存对齐

4.1. 结构体内存对齐

4.2. 修改默认对齐数

5. 结构体传参

6. 结构体实现位段(位段的填充&可移植性)

6.1. 什么是位段

6.2. 位段的内存分配 

2. 枚举

2.1. 枚举

2.2. 枚举常量的优点

3. 联合

3.1. 联合

3.2. 判断大小端

3.3. 联合大小的计算


1. 结构体

首先,为什么要求这些自定义类型呢?在C语言中,其标准已经为我们提供了许多的内置类型,int、char、double等等,但是,有些情况下,人们发现单单靠这些内置类型无法满足现实世界各种复杂的情况,例如,如果我们要描述一本书,我们是不是应该描述它的书名、作者、出版社等等各种信息,我们发现如果此时只有内置类型,是不可能达成类似这种复杂需求的,于是C赋予了程序员们一种权利,可以定义自定义类型。而结构体就是自定义类型中的一种。

1.1. 结构体类型的声明

首先,我们看看结构体的声明是怎样的呢?

// 假如我要描述一本书
struct book
{char book_name[20];char author_name[10];int book_pages;//... 各种信息
};   // 注意这里的 ; 不可漏掉

上面的struct book就是一个结构体类型的声明。

1.1.1. 特殊声明

在声明结构体类型时,C标准允许可以不完全声明:

struct
{int x;int y;int z;
}target;     //并且此时只能在这里定义这个类型的变量struct
{int x;int y;int z;
}*p;   // 定义了一个这个结构体类型指针的变量

上面的类型就是一个匿名结构体类型,有人看到这两个匿名结构体的成员变量完全一样,那能不能这样做呢?

void Test1(void)
{p = &x;
}

首先,这样做是不好的。编译器对于匿名结构体的处理是:如果匿名结构体的成员变量一样,编译器也认为它们是不同的类型。

2. 结构的自引用

结构体的自引用简单理解就是:结构体类型中的成员变量包含一个结构体类型的指针变量。

struct Node
{int val;struct Node next;
};

上面的代码可行吗? 答案是,不可行。为什么呢?struct Node这个自定义类型中包含一个类型为struct Node的成员next,而这个成员也是一个自定义类型struct Node,那它里面也有一个struct Node类型的成员啊,这种无穷包含自己,在编译器看来是一种非法行为,因为此时这个类型的大小无法确定。

正确的自引用是这样的: 

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

而我们直到typedef可以对一种数据类型进行重命名,那么它可以对结构体类型重命名吗?当然可以。例如如下:

typedef struct Node
{int val;struct Node* next;
}Node;

上面的代码的意思是什么呢?就是定义了一个struct Node的结构体类型,我们对这个结构体类型重命名为 Node,即 Node 等价于 struct Node,它们代表着同一种类型。 

然而,有人看到这里就会突发奇想,他说既然Node和 struct Node代表着同一种类型,那么可不可以这样呢?

typedef struct Node
{int val;Node* next;
}Node;  // 走到这里才代表对struct Node进行重命名为 Node

首先,说答案,上面这种声明是非法的,因为此时的这个成员变量next的类型还没有完成重命名,也就是先有鸡还是先有蛋的问题,只有走完这个类型重命名语句,才会对struct Node进行重命名为 Node,不可以在重命名之前就使用重命名后的类型。因此正确的命名是如下这种形式:

typedef struct Node
{int val;struct Node* next;
}Node;

然而,有时候我们会在书中看到这样的声明风格:

typedef struct Node
{int val;struct Node* next;
}Node,*PNode;

其实很简单,

这里的Node等价于struct Node

PNode就相当于 struct Node*

3. 结构体变量的定义和初始化

struct Book
{char book_name[20];int book_pages;double price;
}b1 = {"XqianC语言",531,33.3};    // 声明 + 定义了一个struct Book类型的全局变量struct Node
{struct Book b;struct Node* next;
}n1 = { {"wangwuLinux",843,55.5},NULL };    // 定义一个结构体嵌套类型的全局变量void Test3(void)
{struct Book b2;  // 定义一个struct Book类型的局部变量// 给b2的成员变量赋值b2.book_name[20] = "lisiC++";b2.book_pages = 632;b2.price = 44.4; // 定义 + 初始化struct Node n2 = { { "cuihuaMySql", 483, 66.6 }, NULL };   // 定义一个结构体嵌套类型的局部变量 
}

4. 结构体内存对齐

4.1. 结构体内存对齐

首先,我们看看下面的这两个类型:

struct type1
{char ch1;int i;char ch2;
};struct type2
{char ch1;char ch2;int i;
};

现在要求我们计算着两个类型分别是多大,即:

void Test4(void)
{printf("struct type1 size = %d\n", sizeof(struct type1));printf("struct type2 size = %d\n", sizeof(struct type2));
}

有人一看这两个类型,诶,这两个类型的成员变量除了顺序不一样,其他都是一样的啊,让我算的话,它们的大小难道不应该是 两个 char + 一个 int类型的大小,即等于6吗?OK,让我们看看它的结果是多少呢?

出人意料,诶,怎么回事,不是6就是算了,怎么这两个类型的大小还不一样。为了解决这个问题,我们要引出一个东西,称之为结构体内存对齐规则。

那么结构体内存对齐规则是什么呢?如下:

1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的对齐数 与 该成员变量类型大小 的较小值
vs下默认对齐数为8
3. 结构体总大小为最大对齐数的整数倍。
最大对齐数:所有成员变量类型大小中的最大值。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

有了结构体内存对齐规则,我们就可以分析上面这两个结构体类型的大小为何如此了,分析如下:

声明:为了更好理解下面的图,由于结构体对齐规则导致没有使用的空间用红色表示

对struct type1的分析如下: 

对struct type2的分析如下: 

有了对上面的理解,我们试着去计算下面这个结构体类型的大小:

struct type3
{char ch1struct type2 t2;char ch2;int i;
};

为了验证上面结构体成员的偏移量是否与预期正确,我们可以用offsetof,它是一个宏,可以帮助我们计算一个结构体中某个成员变量相对于起始位置的偏移量。

// 原型如下
// 其包含在 #include <stddef.h>
// structName --- 结构体名字
// memberName --- 成员变量名字
size_t offsetof( structName, memberName );
void Test5(void)
{printf("ch1 offset: %d\n", offsetof(struct type3, ch1));printf("t2 offset: %d\n", offsetof(struct type3, t2));printf("ch2 offset: %d\n", offsetof(struct type3, ch2));printf("i offset: %d\n", offsetof(struct type3, i));
}

结果如下: 

很显然,符合我们的预期。

那么为什么要存在内存对齐:

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说,结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,如果我们既要满足对齐,又要节省空间,该如何做呢?

答案是:让占用空间小的结构体成员尽量集中在一起,例如:

struct type1
{char ch1;int i;char ch2;
};struct type2
{char ch1;char ch2;int i;
};

struct type1和struct type2的成员变量是一样的,但是它们的大小确是不一样的,前者占12个字节,后者占8个字节,原因就在于struct type2中的较小成员集中在了一起,节省了一定的空间。

4.2. 修改默认对齐数

我们知道,VS下的默认对齐数是8,但是我们却可以显式的修改其默认对齐数。

// 编译器的默认对齐数为8
struct type5
{                 char ch;   double d;  
};

#pragma pack(4)   // 将编译器的对齐数修改为4
struct type5
{char ch;double d;
};
#pragma pack()   //  取消设置的对齐数,还原为默认对齐数

#pragma pack(1)   // 将编译器的对齐数修改为1,就意味着不对齐.此时的大小就是9
struct type5
{char ch;double d;
};
#pragma pack()   //  取消设置的对齐数,还原为默认对齐数

结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数。  

5. 结构体传参

struct Info
{int data[1000];char* name[1000];
};// 结构体传参
void print1(struct Info tmp)
{//...
}// 结构体的地址传参
void print2(struct Info* p_tmp)
{//...
}void Test7(void)
{struct Info information = { { 1, 2, 3 }, {NULL} };print1(information);  // 不推荐,值传递传参的压栈系统开销过大print2(&information); // 推荐,址传递传参的压栈系统开销很小
}

对于结构体的传参,我们推荐采用以传结构体地址的方式;因为函数在传参的时候,会将其参数压栈,在时间上和空间上都会有消耗,如果我们传递一个结构体对象,当这个结构体很大的时候,参数压栈的系统开销就会很大,会导致性能的降低,而如果传递一个结构体指针,其大小是固定的(32位下4个字节、64位下8个字节),可以节省系统的开销,一定程度上提高性能。

结论:结构体传参尽量传结构体的地址。

6. 结构体实现位段(位段的填充&可移植性)

6.1. 什么是位段

1. 位段的成员必须是 int unsigned int signed int或者char(char也是属于整形家族)
2. 位段的成员名后边有一个冒号和一个数字。
struct bit
{int a : 2;int b : 5;int c : 10;int d : 30;
};

上面就是一个位段,我们可以看到,位段的成员后面有一个冒号和一个数字,这个数字代表着你这个成员占用了几个bit位,例如:a这个成员就会占用2个bit位。那么位段的大小如何计算呢?位段需要进行内存对齐吗?

首先,对于位段我们应该知道,其主要作用是:节省空间;而我们知道结构体的内存对齐是以空间换取时间的一种方式,很显然,这就与位段的初衷相矛盾了,故位段是不会有内存对齐的。

void Test8(void)
{printf("bit size = %d\n", sizeof(struct bit));
}

那么上面这个位段是多大呢?

可以看到这个位段共占用了8个字节,的确节省了空间。那么位段究竟是如何分配内存的呢?

6.2. 位段的内存分配 

1. 位段的成员可以是 int、unsigned int、 signed int、  或者是 char( 属于整形家族)类型
2. 位段的空间上是按照需要以 4 个字节( int )或者 1 个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

之所以说位段是不跨平台的,是由于当出现上面这种情况时,我们不知道它的内存分配是怎样的,例如:上面剩余了15个bit位,d到底有没有使用它,是不确定的,标准并没有明确规定是否使用这个15个bit。因此,对于位段的使用要小心,如果要求程序具有移植性,那么尽量减少位段的使用。

位段的跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分,配标准尚未定义(vs下是从右向左分配的)。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的(vs下没有利用剩余的空间)

总结:

位段较结构体相比,位段可以达到同样的效果,但是可以很好的节省空间,但由于标准对许多细节并没有明确规定,存在跨平台的问题,其可移植性是有待商榷。

2. 枚举

2.1. 枚举

枚举(enum)是一种用于定义一组相关常量的数据类型。它可以帮助开发人员更清晰地表示某个值的取值范围,并提供更好的可读性和可维护性。

在枚举类型中,我们可以定义一组具体的命名常量,也称为枚举成员。每个枚举成员都有一个与之关联的值,它可以是数字(如整数)或者是其他数据类型(如字符串)。枚举成员之间用逗号隔开。

使用枚举,我们可以通过给定的枚举成员来表示某个特定的取值。这有助于编写更可读的代码,并减少硬编码所带来的错误。此外,枚举还可以作为函数参数、变量和属性的类型,增加代码的类型安全性。

enum color
{RED,YELLOW,BILU
};

上面的enum color 就是一种枚举类型,{}中的内容是枚举类型的可能取值,也称之为枚举常量,这些枚举常量都是有值的,默认从0开始,依次递增1,当然在定义的时候也可以赋初值,例如:

enum color
{RED = 5,YELLOW = 3,BLACK,   // 这里的BLACK没有赋初值,那么就是YELLOW + 1,即等于4BILU = 7   // 注意,最后一项的枚举常量不加,
};

2.2. 枚举常量的优点

为什么要使用枚举常量呢?它与#define定义的符号常量有什么区别呢?

枚举常量的优点:

1. 增加代码的可读性和可维护性
2. #define 定义的标识符比较枚举有类型检查,更加严谨。
枚举常量是属于一种枚举类型的,它是具有类型检查的,与符号常量相比更为严谨
3. 防止了命名污染(封装)
4. 便于调试,宏定义的符号常量在预编译阶段就被替换了。
5. 使用方便,一次可以定义多个常量

因此,我们推荐使用枚举常量,以减少宏定义的标识符的使用

3. 联合

3.1. 联合

联合(union)是一种特殊的 自定义类型 ,它可用于在同一内存空间中存储不同的数据类型。它允许使用同一块内存来存储多种类型的数据,但同一时间只能使用其中的一种类型数据。

联合与结构体非常相似,都可以定义多个成员,但联合只分配给它成员中最大的元素所需要的内存空间(需要考虑内存对齐,当最大成员的大小不是最大对齐数的整数倍时,就需要内存对齐)。因此,联合可以通过不同的成员来表示同一块内存中的数据,这使得它在一定程度上可以节省内存空间。

联合体的特征就是:联合的成员共用同一块空间(所以联合也叫共用体),示例如下:

union Un
{char ch;int i;
};

那么上面这个联合体的大小是多少呢?

void Test10(void)
{printf("Un size: %d\n", sizeof(union Un));
}

联合的大小是由其最大的成员决定的,例如上面的这个联合,其最大成员是一个int类型,又因为此时这个联合体的最大对齐数就是4,因此上面这个联合的大小就是4。 

void Test11(void)
{union Un u;printf("u address: %p\n", &u);printf("ch address: %p\n", &(u.ch));printf("i address: %p\n", &(u.i));
}

可以发现,联合对象和它的成员的地址是一样的,也就是说,其内存分配如图所示:

联合的特定是成员共享同一块空间,但这也限制了在同一时刻只可以使用一种成员。例如:

union Un
{char ch;int i;
};
void Test12(void)
{union Un u;u.i = 0x11223344;u.ch = 0x66;
}

在使用联合时,改变其中一个成员变量,另一个成员变量也会被修改,因此一般情况下,联合在某一时刻下是单独使用一个成员变量的 。

3.2. 判断大小端

什么叫大端,什么叫小端呢?

大端(Big Endian)和小端(Little Endian)是用于描述存储和处理多字节数据的方式。

大端字节序(Big Endian)是指将最高有效字节(Most Significant Byte,MSB)存储在最低地址处,最低有效字节(Least Significant Byte,LSB)存储在最高地址处。也就是说,数据的高位字节存储在低位地址,低位字节存储在高位地址。

小端字节序(Little Endian)则相反,它是指将最低有效字节(LSB)存储在最低地址处,最高有效字节(MSB)存储在最高地址处。也就是说,数据的低位字节存储在低位地址,高位字节存储在高位地址。

void Test13(void)
{int i = 0x12345678;// 对于0x12345678的大端字节序和小端字节序// 低地址 <---------------> 高地址// 大端字节序:// 0x12 0x34 0x56 0x78// 小端字节序:// 0x78 0x56 0x34 0x12
}

利用联合的特性(其成员共有同一块空间),我们就可以判断某个机器下是大段还是小端存储,那么如何判断呢?

union Un
{char ch;int i;
};void Test14(void)
{union Un u;u.i = 1;// 此时如果u.ch == 1,那么就是小端;如果u.ch == 0,那么就是大端if (u.ch == 1)printf("小端存储\n");if (u.ch == 0)printf("大端存储\n");
}

 

3.3. 联合大小的计算

union Un1
{char ch[5];int i;
};

上面的联合体是多大呢?注意,联合体的的大小首先是要保证能够存放最大成员,其次如果最大成员所占空间大小不是其最大对齐数的整数倍,那么需要内存对齐。例如上面的:

至此,C语言的自定义类型到此结束。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/122795.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Glide原理

本文基于Carson整理 1.简介 相比其他几种图片加载框架&#xff0c;Glide性能最好。这得益于其高效的图片缓存策略 其还有多样化的媒体格式加载&#xff1a;如GIF、Video&#xff0c;对于商城首页需展示丰富样式、信息的页面需求来说&#xff0c;也是必不可少的。 2.加载原理…

nodejs+vue+elementui+express酒店管理系统

登录&#xff1a;运行系统后&#xff0c;进行登录&#xff0c;可使用本系统。 客房预定&#xff1a;此界面先通过条件查询客房信息&#xff0c;然后进行客房预定。对预定的客房还可以取消和支付操作。 信息查询&#xff1a;可查询所有的公告信息&#xff0c;点击公告名称&#…

[量化投资-学习笔记003]Python+TDengine从零开始搭建量化分析平台-Grafana画K线图

在前面两个笔记&#xff1a; PythonTDengine从零开始搭建量化分析平台-数据存储 PythonTDengine从零开始搭建量化分析平台-MA均线的多种实现方式 中有提到使用 Grafana 画图&#xff0c;不过画的都是均线。除了均线&#xff0c;Grafana 非常人性的提供了 K线图模块 搭配 TDeng…

基于群居蜘蛛算法的无人机航迹规划

基于群居蜘蛛算法的无人机航迹规划 文章目录 基于群居蜘蛛算法的无人机航迹规划1.群居蜘蛛搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用群居蜘蛛算法来优化无人机航迹规划。 …

Photoshop(PS)安装教程(2023最新最详细图文教程)

目录 一.简介 二.安装步骤 软件&#xff1a;PS版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;3.20G系统要求&#xff1a;Win10&#xff08;1903&#xff09;及以上版本&#xff0c;64位操作系统硬件要求&#xff1a;CPU2.0GHz 内存8G(或更高&#xff0c;不支持…

5G 3GPP全球频谱介绍

所谓 “频谱”&#xff0c;是指特定类型的无线通信所在的射频范围。不同的无线技术使用不同的频谱&#xff0c;因此互不干扰。由于一项技术的频谱是有限的&#xff0c;因此频谱空间存在大量竞争&#xff0c;并且人们也在不断开发和增强全新的、高效率的频谱使用方式。 介绍5G …

Vue echarts 折线图 背景颜色渐变 (两种实现方式)

需求 实现方式 两种方法 方法一&#xff1a;color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{}&#xff0c;{}&#xff0c;{}]) 方法二&#xff1a;避开new echarts&#xff0c;color: {x: 0, y: 0, x2: 0, y2: 1,colorStops: [{}&#xff0c;{}&#xff0c;{}]} …

Linux(Centos7)操作记录

1、nginx -t #Nginx配置文件检查 上述截图代表检查没问题 上述截图检查配置文件配置错误&#xff0c;并提示错误文件位置 2、systemctl restart nginx #重启Nginx 重启Nginx失败 3、systemctl status nginx.service #查看Nginx服务状态 80端口被占导致服务启动失败 4、n…

k8s 金丝雀发布与声明式管理

Deployment控制器支持自定义控制更新过程中的滚动节奏&#xff0c;如“暂停(pause)”或“继续(resume)”更新操作。比如等待第一批新的Pod资源创建完成后立即暂停更新过程&#xff0c;此时&#xff0c;仅存在一部分新版本的应用&#xff0c;主体部分还是旧的版本。然后&#xf…

基于水循环算法的无人机航迹规划-附代码

基于水循环算法的无人机航迹规划 文章目录 基于水循环算法的无人机航迹规划1.水循环搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用水循环算法来优化无人机航迹规划。 1.水循环…

【赠书活动】从瀑布模式到水母模式:ChatGPT如何赋能软件研发全流程

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

YOLOv5算法 | 万字长文带你深度解析yolov5s.yaml配置文件

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。配置文件yolov5s.yaml在YOLOv5模型训练过程中发挥着至关重要的作用&#xff0c;属于初学者必知必会的文件&#xff01;在YOLOv5-6.0版本源码中&#xff0c;配置了5种不同大小的网络模型&#xff0c;分别是YOLOv5n、YOLOv5s…

使用GoQuery实现头条新闻采集

概述 在本文中&#xff0c;我们将介绍如何使用Go语言和GoQuery库实现一个简单的爬虫程序&#xff0c;用于抓取头条新闻的网页内容。我们还将使用爬虫代理服务&#xff0c;提高爬虫程序的性能和安全性。我们将使用多线程技术&#xff0c;提高采集效率。最后&#xff0c;我们将展…

Linux中shell脚本中的运算

目录 一、运算符号 二、运算指令 三、练习 一、运算符号 加法-减法*乘法/除法%除法后的余数**乘方自加一--自减一 <小于<小于等于>大于>大于等于等于ji&#xff0c;jji*jj*i/jj/i%jj%i 二、运算指令 (()) ##((a12)) let ##let a12 expr ##expr 1 2 …

0030Java程序设计-积分管理系统论文

文章目录 摘  要**目  录**系统实现系统功能需求3.2.1 管理员功能3.2.2 柜员功能 开发环境 摘  要 随着计算机和网络的不断革新&#xff0c;世界已经进入了前所未有的电子时代。作为实用性强、应用范围广泛的会员管理系统也正在被越来越多的各类企业用于消费管理领域。然…

竞赛 深度学习大数据物流平台 python

文章目录 0 前言1 课题背景2 物流大数据平台的架构与设计3 智能车货匹配推荐算法的实现**1\. 问题陈述****2\. 算法模型**3\. 模型构建总览 **4 司机标签体系的搭建及算法****1\. 冷启动**2\. LSTM多标签模型算法 5 货运价格预测6 总结7 部分核心代码8 最后 0 前言 &#x1f5…

Spring Boot 配置邮件发送服务

文章归档&#xff1a;https://www.yuque.com/u27599042/coding_star/ctwkrus1r9zrytsq spring boot 版本 3.1.3 邮件发送服务使用的 QQ 邮箱提供的 依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent…

CNCC2023

中国工程院院士&#xff0c;之江实验室主任、阿里云创始人王坚&#xff1a;计算驱动的科学发现和科技创新。 国际计算机学会主席雅尼斯约阿尼迪斯(ACM President Yannis Ioannidis)。 电气和电子工程师协会计算机协会主席妮塔帕特尔(IEEE CS President Nita Patel)。 2022 I…

【PWN · heap | Off-By-One】Asis CTF 2016 b00ks

萌新进度太慢了&#xff0c;才真正开始heap&#xff0c;还是从简单的Off-By-One开始吧 前言 步入堆的学习。堆的知识复杂而多&#xff0c;于是想着由wiki从简单部分逐个啃。 b00ks是经典的堆上off-by-one漏洞题目。刚开始看很懵&#xff08;因为确实连堆的管理机制都没有完全…

windows8080端口占用

查看端口占用 netstat -ano | findstr “8080”查看占用进程 tasklist | findstr “4664”关闭占用进程 taskkill /f /t /im httpd.exe