C语言学习汇总(九)----结构体与内存管理

本文详细讲解了C语言中结构体的定义、初始化、类型大小、指针的应用、typedef类型重命名以及内存管理,包括栈区与堆区的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

结构体

定义结构体变量

结构体初始化和引用

结构体类型大小

 结构体指针

typedef 类型另起名函数

结构体复制

内存管理

栈区、堆区

栈内存


结构体

在 C 语言中,char、int、float 等属于系统内置的基本数据类型,往往只能解决简单的问题。当遇到比较复杂的问题时,只使用基本数据类型是难以满足实际开发需求的。因此,C 语言允许用户根据实际项目需求,自定义一些数据类型,并且用它们来定义变量。

前面文章中介绍了 C 语言的多种数据类型,例如,整型、字符型、浮点型、数组、指针等。但是在实际开发中,只有这些数据类型还难以胜任复杂的程序设计。例如,在员工信息管理系统中,员工的信息就是一类复杂的数据。每条记录中都包括员工的姓名、年龄、工号、工资等信息。姓名为字符数组,年龄为整型,工号为整型,工资为整型。

对于这类数据,显然不能使用数组存储,因为数组各个元素的类型都是相同的。为了解决这个问题,C 语言提供了一种组合数据类型—结构体。

结构体是一种组合数据类型,由用户自己定义。结构体类型中的元素既可以是基本数据类型,也可以是结构体类型。

定义结构体类型的一般格式为:

struct 结构体名

{

成员列表

};

成员列表由多个成员组成,每个成员都必须作类型声明,成员声明的格式为:

数据类型 成员名;

下面来看一个具体的例子。

struct Employee

{

char    name[8];

int     age;

int     id;

int     salary;

};

这段代码中 struct 是关键字,Employee 是结构体名,struct Employee 表示一种结构体类型。

该结构体中有 4 个成员,分别为 name、age、id、salary,使用这种结构体类型就可以表示员工的基本信息。

定义结构体变量

C 语言中,定义结构体变量的方式有三种。

第一种,先定义结构体类型,再定义结构体变量

一般形式为:

struct 结构体名

{

成员列表

};

struct 结构体名 变量名;

例如:

struct Employee

{

char  name[8];

int  age;

int  id;

int  salary;

};

struct Employee emp;

这种方式和基本类型的变量定义方式相同,其中 struct Employee 是结构体类型名,emp 是结构体变量名。

第二种,在定义结构体类型的同时定义变量

一般形式为:

struct 结构体名

{

成员列表

}变量名;

例如:

struct Employee

{

char  name[8];

int  age;

int  id;

int  salary;

}emp;

这种方式将结构体类型定义与变量定义放在一起,可以直接看到结构体的内部结构,比较直观。

第三种,直接定义结构体变量,不需指定结构体名

一般形式为:

struct

{

成员列表

}变量名;

例如:

struct

{

char    name[8];

int     age;

int     id;

int     salary;

}emp;

这种方式由于没有指定结构体名,显然不能再使用该结构体类型去定义其他变量,在实际开发中很少用到。

结构体初始化和引用

1.结构体变量初始化

在 C 语言中,结构体变量初始化,本质上是对结构体变量中的成员进行初始化,方式是使用「{ }」在初始化列表中对结构体变量中各个成员进行初始化,例如:

struct Employee emp={"rupeng",20,1,10000}

struct Employee

{

char  name[8];

int  age;

int  id;

int  salary;

}emp={"rupeng",20,1,10000};

编译器会将「rupeng」、20、1、10000 按照顺序依次赋值给结构体变量 emp 中的成员 name、age、id、salary。

2.引用结构体变量

引用结构体变量的本质,是引用结构体变量中不同类型的成员,引用的一般形式为:

结构体变量名.成员名;

例如,emp.name 表示引用 emp 变量中的 name 成员,emp.id 表示引用 emp 变量中的 id 成员。

其中「.」是成员运算符,它的优先级在所有运算符中是最高的。

下面通过例子来了解结构体变量的初始化和引用。

例:结构体变量的初始化和引用。

01 #include<stdio.h>

02 struct Employee

03 {

04   char  name[8];

05   int  age;

06   int  id;

07   int  salary;

08 };

09 int main(void){

10   struct Employee emp={"rupeng",20,1,10000};

11   printf("%s\n",emp.name);

12   printf("%d\n",emp.age);

13   printf("%d\n",emp.id);

14   printf("%d\n",emp.salary);

15   getchar();

16   return 0;

17 }

运行结果如图

【程序分析】

(1)第 2~8 行,定义结构体类型 struct Employee,成员分别为 name、age、id、salary。

(2)第 10 行,定义 struct Employee 类型变量 emp,并使用初始化列表对成员进行初始化。

(3)第 11~14 行,分别输出 emp 中各个成员的值。

除了采用初始化列表,还可以使用赋值运算符,对成员进行初始化。

结构体类型大小


结构体类型大小,也即结构体类型所占字节数,就是结构体各个成员类型所占字节数的总和,例如:

struct Employee

{

char  name[8]

int   age;

int   id;

int   salary;

};

其中 name 是长度为 8 的字符数组,占 8 个字节,age 是 int 类型,占 4 个字节,id 是 int 类型,占 4 个字节,salary 是 int 类型,占 4 个字节。因此,struct Employee 类型所占字节数为 8+4+4+4=20 个字节。

下面通过例子来了解一下。

例:计算结构体类型大小。

01 #include<stdio.h>

02 struct Employee

03 {

04   char  name[8];

05   int   age;

06   int   id;

07   int   salary;

08 };

09 int main(void)

10 {

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

12   getchar();

13   return 0;

14 }

运行结果如图

 

程序分析:

1.第 2~8 行,定义结构体类型 struct Employee。

2.第 11 行,使用 sizeof 函数计算 struct Employee 类型所占字节数,并输出结果 20。

 结构体指针

指向结构体变量的指针就是结构体指针,如果指针变量中保存一个结构体变量的地址,则这个指针变量指向该结构体变量,需要注意的是指针变量的类型必须和结构体变量的类型相同。

定义结构体指针变量的一般形式为:

struct 结构体名 *指针变量名

例如:

struct Employee emp;

struct Employee * p_emp=&emp;

其中 emp 为结构体变量,p_emp 为结构体指针,将 emp 取地址赋给指针变量 p_emp,表示 p_emp 指向 emp。

在 C 语言中,通过结构体指针 p 也可以引用结构体中的成员,主要有以下两种方式:

(1)(*p).成员名;

(2)p→ 成员名。

例如:

struct Employee * p_emp=&emp;

(*p_emp)表示指向的结构体变量 emp,(*p_emp).age 表示指向的结构体变量 emp 中的成员 age。注意,「.」运算符优先级是最高的,(*p_emp)两侧的括号不能省略。

为了简化操作,C 语言允许将「(*p).成员名」用「p-> 成员名」替换,(*p_emp).age 等价于 p_emp->age,「->」称为指向运算符。

typedef 类型另起名函数


在 C 语言中,除了使用 C 语言提供的标准类型名:char、int、double 以及自定义的结构体类型,还可以使用 typedef 关键字指定一个新的类型名来代替已有的类型名,相当于给已有类型另起名。类似于现实生活中,给一个人起外号一样。

typedef 的一般使用形式为:

typedef 原类型名 新类型名

例如:

typedef int integer

其中 integer 是 int 类型的别名

结构体复制

在 C 语言中,允许相同类型的结构体变量之间相互赋值。

例如:

t_Employee emp={"rupeng",20,1,10000};

t_Employee emp2=emp;

执行 emp2=emp,会将结构体变量 emp 中各个成员的值复制一份到变量 emp2 各个成员中。和基本类型变量的赋值规则相同,emp2 是 emp 的一个复制体。这种赋值方式,被称为「结构体复制」。

内存管理

内存管理
在 C 语言中,当一个程序被加载到内存中运行时,系统会为该程序分配一块独立的内存空间,并且这块内存空间又可以再被细分为很多区域,如栈区、堆区、静态区、全局区等。本章将介绍内存空间中常用的区域:栈区、堆区。

栈区、堆区

在 C 语言程序中,栈区、堆区是最常使用的内存区域。

栈区:保存局部变量。存储在栈区的变量,在函数执行结束后,会被系统自动释放。

堆区:由 malloc、calloc、realloc 等函数分配内存。其生命周期由 free 函数控制,在没有被释放之前一直存在,直到程序运行结束。

栈内存


定义在函数内部的局部变量,都保存在栈区。栈区的特点是:函数执行结束后,系统会「自动回收」局部变量所对应的内存空间。所谓的「自动回收」其实是操作系统将这块栈内存又分配给其他函数中的局部变量使用。

可以将栈区比作餐厅,局部变量比作客人,局部变量对应的栈内存比作餐具。客人吃饭时使用的餐具,在客人离开后由餐厅负责回收、清洗,然后再给其他客人使用。同理,局部变量与栈内存的关系也是如此,当定义局部变量时,系统会在栈区为其分配一块内存空间,当函数执行结束后系统负责回收这块内存,再分配给其他局部变量使用。

由于局部变量在函数执行结束后,会被系统「自动回收」并分配给其他函数中的局部变量使用。因此,在 C 语言程序中,不能将局部变量地址作为函数返回值,否则会出现一些错误结果。

使用 malloc 系列函数分配的内存都属于堆区,使用完后需调用 free 函数将其释放,否则可能会造成内存泄漏。

举个例子,堆区相当于自己家,malloc 分配的堆内存相当于盘子。在家里吃饭时使用的盘子,吃完后必须手动进行清洗,否则盘子将不能再使用。同理,堆内存也是如此,使用完毕后,需要调用 free 函数手动释放,否则这块堆内存将无法再次被使用。

malloc 函数

【函数原型】

void *malloc(int size);

【头文件】

#include <stdlib.h>

【形参列表】

size:分配多少个字节。

【函数功能】

申请指定大小的堆内存。

【返回值】

如果分配成功则返回指向被分配内存的指针,否则返回空指针 NULL。

注意:void*表示「不确定指向类型」的指针,使用前必须进行强制类型转换,将 void*转化为「确定指向类型」的指针。

free 函数

【函数原型】

void free(void* ptr);

【头文件】

#include <stdlib.h>

【形参列表】

ptr:指向要被释放的堆内存。

【函数功能】

释放 ptr 指向的内存空间。

栈内存由系统自动分配、释放。常见形式如函数形参、局部变量等。栈内存比较小,在 VS2012 中,栈内存默认最大为 1Mb,如果局部变量占用的栈内存过大,会发生栈溢出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辽G.默语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值