结构体中的malloc 与 free

2024-08-30 17:48
文章标签 结构 free malloc 体中

本文主要是介绍结构体中的malloc 与 free,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  1. 结构体中动态内存的管理(malloc和free)

    C语言中内存的管理主要是依据malloc和free实现的,其中malloc主要是实现内存的分配,而free则是实现内存的释放。虽然这是我们已经很熟悉的,但是还是存在一些问题。特别是当结构体中存在指针的情况下,各种问题也就会展现出来。
    其中最大的问题是:结构体中指针变量没有指向一块合法的内存空间,就对指针参数进行操作,这也是很多C语言程序员经常犯的错误。

    简单的实例如下:
    1. struct student
    2. {
    3.         char *name;
    4.         int score;
    5. }stu,*pstu;

    6. int main()
    7. {
    8.         strcpy(stu.name,"Jimy");
    9.         stu.score = 99;


    10.         strcpy(pstu->name,"Jimy");
    11.         pstu->score = 99;
    12. }


    这种代码是新手经常犯的错误,其中的主要错误是指针变量没有指向一块内存空间,其中包括ptest没有指向一块内存空间,同时结构体中的指针变量name也没有指向一块内存空间。

    这种代码一般都会编译通过,但是运行过程中会发生新手编程经常出现的段错误Segmentation fault (core dumped),我通过gdb对程序进行调试发现了存在的一些问题。其中stu.name中的内容是0x0,也就是地址0x0。这样我就知道了0x0为什么会发生段错误了,因为在Linux中进程都有一个独立的虚拟存储空间4G,但是其中在最底部的0x0是没有映射的,具体的参看进程的存储器映射关系。0x0并没有映射,这样发生段错误也就不奇怪了。

    也就是说指针变量里存储的地址值并不是一个我们需要的值,为了指向一块内存空间,因此需要采用malloc分配一块内存空间。

    改写上面的代码实现内存的分配。
    1. int main()
    2. {
    3.         /*创建一块内存空间,并让stu.name指向这块内存空间*/
    4.         stu.name = (char *)malloc(20*sizeof(char));
    5.         /*实现字符串的复制过程*/
    6.         strcpy(stu.name,"Jimy");
    7.         stu.score = 99;

    8.         /*创建一块内存空间,并让pstu指向这块内存空间*/
    9.         pstu = (struct student *)malloc(sizeof(struct student));
    10.         /*创建一块内存空间,并让pstu->name指向这块内存空间*/
    11.         pstu->name = (char *)malloc(20*sizeof(char));
    12.         /*实现字符串的复制过程*/
    13.         strcpy(pstu->name,"Jimy");
    14.         pstu->score = 99;
    15.     
    16.         return 0;
    17. }

    这样补充以后的代码就为指针变量添加了指向的对象,由于是采用malloc动态申请的存储空间,那么这段存储空间是分配在堆中,而不是在栈中,如果是在被调用函数中申请内存空间,那么在函数返回后该内存空间并不会释放。

    1. Breakpoint 1, main () at TestStructPoint.c:21
    2. 21 stu.name = (char *)malloc(20*sizeof(char));
    3. Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686
    4. (gdb) p stu     ----stu中的内容
    5. $1 = {name = 0x0, score = 0}
    6. (gdb) p stu.name  ----stu.name其中的内容是0x0,也就是指向0x0
    7. $2 = 0x0
    8. (gdb) c
    9. Continuing.
    10. Breakpoint 2, main () at TestStructPoint.c:25
    11. 25 strcpy(stu.name,"Jimy");
    12. (gdb) p stu.name   -----stu.name其中的内容不再是0x0,而是一个地址值,该地值中的内容为空
    13. $3 = 0x804a008 ""
    14. (gdb) c
    15. Continuing.
    16. Breakpoint 3, main () at TestStructPoint.c:26
    17. 26 stu.score = 99;
    18. (gdb) p stu.name    -----stu.name中存储的地址的内容发生了变化。
    19. $4 = 0x804a008 "Jimy"
    20. (gdb) c
    21. Continuing.
    22. Breakpoint 4, main () at TestStructPoint.c:29
    23. 29 pstu = (struct student *)malloc(sizeof(struct student));
    24. (gdb) p pstu        ----pstu指向的地址也是0x0
    25. $5 = (struct student *) 0x0
    26. (gdb) c
    27. Continuing.
    28. Breakpoint 5, main () at TestStructPoint.c:32
    29. 32 pstu->name = (char *)malloc(20*sizeof(char));
    30. (gdb) p pstu    ----pstu指向的内存地址发生了改变,不再是0x0
    31. $6 = (struct student *) 0x804a020
    32. (gdb) c
    33. Continuing.
    34. Breakpoint 6, main () at TestStructPoint.c:35
    35. 35 strcpy(pstu->name,"Jimy");
    36. (gdb) p pstu
    37. $7 = (struct student *) 0x804a020
    38. (gdb) p pstu->name   ----pstu->name中的地址也不再是0x0,而是一个非零的地址值
    39. $8 = 0x804a030 ""
    40. (gdb) c
    41. Continuing.
    42. Breakpoint 7, main () at TestStructPoint.c:36
    43. 36 pstu->score = 99;
    44. (gdb) p pstu->name
    45. $9 = 0x804a030 "Jimy"  ----pstu->name指向地址中的内容发生改变
    46. (gdb) c
    47. Continuing.
    48. Program exited normally.

    根据上面的调试可以知道,指针变量在定义过程中没有初始化为NULL,则指针变量指向的地址就是0x0,而在Linux中的进程虚拟存储器映射中地址0x0并没有映射,因此会出现错误。因此结构体中的指针变量一定要指向一块具体的存储空间之后才能进行相应的操作。同时其他的指针也必须指向相应的地址以后再操作。

    但是分配完地址后还需要在相应操作结束后释放分配的存储器,不然会造成浪费,以及内存的泄漏。这也是很多程序员忘记完成的工作。

    内存的释放采用free函数即可,free函数是将分配的这块内存与指针(malloc返回的指针)之间的所有关系斩断,指针变量P中存储的地址(这块内存的起始地址)值也没有发生变化,同时存储器中存储的内容也并没有发生改变,改变的只是指针对这块内存地址的所有权问题。但是该起始地址所在内存中的数据内容已经没法使用了,即时采用其他的指针也不能访问。如果下一次调用malloc函数可能会在刚才释放的区域创建一个内存空间,由于释放以后的存储空间的内容并没有改变(我是参考书上的,但我认为free后存储器中的内容是发生变化的,后面的调试可以说明这个问题,只是不知道发生什么变化,我也只是猜测,但是不要访问这个存储空间的内容是最安全的),这样可能会影响后面的结果,因此需要对创建的内存空间进行清零操作(防止前面的操作影响后面),这通常采用memset函数实现,具体参看memset函数。还有指针变量P中存储的地址值并没有改变,由于指针P没有对这个地址的访问权限,程序中对P的引用都可能导致错误的产生,造成野指针,因此最后还需要将指针P指向NULL,避免野指针的产生。当然也需要对创建是否成功需要检测,但这里我暂时不考虑这些错误的处理。




  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>

  5. struct student
  6. {
  7.         char *name;
  8.         int score;
  9. }stu,*pstu;


  10. int main()
  11. {
  12.         /*为name分配指向的一段内存空间*/
  13.         stu.name = (char *)malloc(20*sizeof(char));
  14.         memset(stu.name,0,20*sizeof(char));

  15.         strcpy(stu.name,"Jimy");
  16.         stu.score = 99;

  17.         /*为pstu分配指向的一段内存空间*/
  18.         pstu = (struct student *)malloc(sizeof(struct student));
  19.         memset(pstu,0,sizeof(struct student));

  20.         /*为name分配指向的一段内存空间*/
  21.         pstu->name = (char *)malloc(20*sizeof(char));
  22.         memset(pstu->name,0,20*sizeof(char));

  23.         strcpy(pstu->name,"Jimy");
  24.         pstu->score = 99;

  25.         /*采用另外的指针访问分配的存储空间,测试内存中内容是否改变*/
  26.         char *= stu.name;
  27.         char *p1 = (char *)0x804a008;//具体的地址值 
  28.         char *ppstu = pstu->name;
  29.         char *pp = (char *)0x804a030;//具体的地址值

  30.         /*释放的顺序要注意,pstu->name必须在pstu释放之前释放,
  31.          *如果pstu先释放,那么pstu->name就不能正确的访问。
  32.         */
  33.         free(pstu->name);
  34.         free(stu.name);
  35.         free(pstu);

  36.         /*为了防止野指针产生*/
  37.         pstu->name = NULL;
  38.         stu.name = NULL;
  39.         pstu = NULL;
  40.         p = NULL;
  41.         ppstu = NULL;

  42.         return 0;
  43. }
下面的全部是调试结果,根据调试结果说明问题:


具体的调试过程说明了其中很多的问题,根据其中的结果可以知道,free结束以后指针变量P(malloc返回)中存储的地址值并没有改变,但是通过该地值已经不能访问之前的分配的存储空间。我采用其他的指针(直接赋值地址以及指针赋值)访问得到的结果也不是正确的值,虽然这不能说明地址中的数据改变了,但说明对释放以后的存储空间再通过其他方法访问不会得到正确的值,但是内存空间中存在值,并不一定是0,因此每一次malloc都清零是必要的。防止野指针也是非常必要的,减少程序错误的概率。

最后说明一下,链表作为结构体的衍生产物,链表的结构体中就有指针变量,因此一定草采用malloc等分配好内存块以后,再对链表进行操作,不然都会导致不同问题的产生。

这篇关于结构体中的malloc 与 free的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121511

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符

Science|癌症中三级淋巴结构的免疫调节作用与治疗潜力|顶刊精析·24-09-08

小罗碎碎念 Science文献精析 今天精析的这一篇综述,于2022-01-07发表于Science,主要讨论了癌症中的三级淋巴结构(Tertiary Lymphoid Structures, TLS)及其在肿瘤免疫反应中的作用。 作者类型作者姓名单位名称(中文)通讯作者介绍第一作者Ton N. Schumacher荷兰癌症研究所通讯作者之一通讯作者Daniela S. Thomm

oracle11.2g递归查询(树形结构查询)

转自: 一 二 简单语法介绍 一、树型表结构:节点ID 上级ID 节点名称二、公式: select 节点ID,节点名称,levelfrom 表connect by prior 节点ID=上级节点IDstart with 上级节点ID=节点值 oracle官网解说 开发人员:SQL 递归: 在 Oracle Database 11g 第 2 版中查询层次结构数据的快速

Tomcat下载压缩包解压后应有如下文件结构

1、bin:存放启动和关闭Tomcat的命令的路径。 2、conf:存放Tomcat的配置,所有的Tomcat的配置都在该路径下设置。 3、lib:存放Tomcat服务器的核心类库(JAR文件),如果需要扩展Tomcat功能,也可将第三方类库复制到该路径下。 4、logs:这是一个空路径,该路径用于保存Tomcat每次运行后产生的日志。 5、temp:保存Web应用运行过程中生成的临时文件

C和指针:结构体(struct)和联合(union)

结构体和联合 结构体 结构体包含一些数据成员,每个成员可能具有不同的类型。 数组的元素长度相同,可以通过下标访问(转换为指针)。但是结构体的成员可能长度不同,所以不能用下标来访问它们。成员有自己的名字,可以通过名字访问成员。 结构声明 在声明结构时,必须列出它包含的所有成员。 struct tag {member-list} variable-list ; 定义一个结构体变量x(包含

Java Web应用程序的推荐目录结构

以前没有用过maven管理过项目的依赖,最后使用上了maven,发现通过不能方式建立出来的web应用程序目录结构基本都不一样,既然每次都要到网上搜索如何建立maven管理的Web应用程序,不如自己找百度谷歌一下。 找了半天 ,感觉比较好的maven管理的web应用程序目录结构是这个: ├── pom.xml└── src├── main│ ├── java│ │ └── myg

mysql 修改表结构语句

主要还是要参考mysql的官方网站 http://dev.mysql.com/doc/refman/5.7/en/alter-table.html 简单例子: alter table_name alter column old_column_name new_column_name int unsigned;