c语言如何实现malloc调试malloc问题

C语言编程(126)
【问题一】:在子函数中动态分配内存:p=(int*)malloc(a*sizeof(int)),这个函数的目的是返回p:return(p)
我想问问,该在什么地方进行free(p)呢?如果在子函数中的return前free是肯定不行的,在之后free?子函数的return之后,不就结束函数调用了吗?那时候free还会被子函数执行吗?
【解答一】:
呃,这个在所分配的内存不需要的时候free,因为你返回的时候这段内存还需要使用,所以在当前函数中不free
【解答二】:
malloc是在堆上分配的空间&&不会随函数结束而消失,自己申请的要自己释放,这就是动态申请。还有不能释放俩次free(p)后&&一定要把p&=&NULL;&不然容易再释放一次&&那就会出异常了
【问题二】:
谢谢楼上诸位,我知道malloc是在堆上分配的内存,不会随函数调用结束而自动释放,问题是如果在被调函数中释放,释放位置无非两个:return前或return后,return前释放是不可能了,不然我这函数白写了,还没传递给正主呢,自己先被干掉了,return后更不可能了,函数见了return就结束了,后面再写多少行都白写
可也不能在主函数中释放啊&我试了试,编译器报错说我释放的东西是未定义的,这是意料之中的
咋办呢?真是迷茫了
你传给函数的时候,在函数体外传的应该是双指针,否则这块内存是返回不了的。或者用函数返回值来传递动态内存
方法一:双指针
GetMemory(char ** p, int num)
& &*p = (char *)malloc(num*sizeof(char));
int main()
& &char *str = NULL;
& &GetMemory(&str,10);
& &strcpy(str, “hello”);
&&free(str);
& &return 0;
方法二:用return
char * GetMemory(int num)
& &char *p = (char *)malloc(num*sizeof(char));
& &return p;
int main()
& &char *str = NULL;
& &str = GetMemory(10);
& &strcpy(str, &“hello”);
&free(str);
& &return 0;
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:151939次
积分:2610
积分:2610
排名:第11765名
原创:54篇
转载:363篇
(1)(3)(6)(1)(8)(7)(1)(2)(10)(2)(31)(25)(8)(19)(25)(31)(44)(63)(31)(29)(15)(34)(21)匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。博客访问: 1056685
博文数量: 160
博客积分: 3730
博客等级: 上尉
技术积分: 3661
注册时间:
减肥,运动,学习,进步...
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
C语言中内存的管理主要是依据malloc和free实现的,其中malloc主要是实现内存的分配,而free则是实现内存的释放。虽然这是我们已经很熟悉的,但是还是存在一些问题。特别是当结构体中存在指针的情况下,各种问题也就会展现出来。其中最大的问题是:结构体中指针变量没有指向一块合法的内存空间,就对指针参数进行操作,这也是很多C语言程序员经常犯的错误。简单的实例如下:struct student
&&&&&&&&char *name;
&&&&&&&&int score;
}stu,*pstu;
int main()
&&&&&&&&strcpy(stu.name,"Jimy");
&&&&&&&&stu.score = 99;
&&&&&&&&strcpy(pstu->name,"Jimy");
&&&&&&&&pstu->score = 99;
}这种代码是新手经常犯的错误,其中的主要错误是指针变量没有指向一块内存空间,其中包括ptest没有指向一块内存空间,同时结构体中的指针变量name也没有指向一块内存空间。这种代码一般都会编译通过,但是运行过程中会发生新手编程经常出现的段错误Segmentation fault (core dumped),我通过gdb对程序进行调试发现了存在的一些问题。其中stu.name中的内容是0x0,也就是地址0x0。这样我就知道了0x0为什么会发生段错误了,因为在Linux中进程都有一个独立的虚拟存储空间4G,但是其中在最底部的0x0是没有映射的,具体的参看进程的存储器映射关系。0x0并没有映射,这样发生段错误也就不奇怪了。也就是说指针变量里存储的地址值并不是一个我们需要的值,为了指向一块内存空间,因此需要采用malloc分配一块内存空间。改写上面的代码实现内存的分配。int main()
{& & & & /*创建一块内存空间,并让stu.name指向这块内存空间*/
&&&&&&&&stu.name = (char *)malloc(20*sizeof(char));& & & & /*实现字符串的复制过程*/
&&&&&&&&strcpy(stu.name,"Jimy");
&&&&&&&&stu.score = 99;
& & & & /*创建一块内存空间,并让pstu指向这块内存空间*/
&&&&&&&&pstu = (struct student *)malloc(sizeof(struct student));& & & & /*创建一块内存空间,并让pstu->name指向这块内存空间*/
&&&&&&&&pstu->name = (char *)malloc(20*sizeof(char));& & & & /*实现字符串的复制过程*/
&&&&&&&&strcpy(pstu->name,"Jimy");
&&&&&&&&pstu->score = 99;
&&&&&&&&return 0;
}这样补充以后的代码就为指针变量添加了指向的对象,由于是采用malloc动态申请的存储空间,那么这段存储空间是分配在堆中,而不是在栈中,如果是在被调用函数中申请内存空间,那么在函数返回后该内存空间并不会释放。Breakpoint 1, main () at TestStructPoint.c:21
stu.name = (char *)malloc(20*sizeof(char));
Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686
(gdb) p stu & & ----stu中的内容
$1 = {name = 0x0, score = 0}
(gdb) p stu.name &----stu.name其中的内容是0x0,也就是指向0x0
Continuing.
Breakpoint 2, main () at TestStructPoint.c:25
strcpy(stu.name,"Jimy");
(gdb) p stu.name & -----stu.name其中的内容不再是0x0,而是一个地址值,该地值中的内容为空
$3 = 0x804a008 ""
Continuing.
Breakpoint 3, main () at TestStructPoint.c:26
stu.score = 99;
(gdb) p stu.name & &-----stu.name中存储的地址的内容发生了变化。
$4 = 0x804a008 "Jimy"
Continuing.
Breakpoint 4, main () at TestStructPoint.c:29
pstu = (struct student *)malloc(sizeof(struct student));
(gdb) p pstu & & & &----pstu指向的地址也是0x0
$5 = (struct student *) 0x0
Continuing.
Breakpoint 5, main () at TestStructPoint.c:32
pstu->name = (char *)malloc(20*sizeof(char));
(gdb) p pstu & &----pstu指向的内存地址发生了改变,不再是0x0
$6 = (struct student *) 0x804a020
Continuing.
Breakpoint 6, main () at TestStructPoint.c:35
strcpy(pstu->name,"Jimy");
(gdb) p pstu
$7 = (struct student *) 0x804a020
(gdb) p pstu->name & ----pstu->name中的地址也不再是0x0,而是一个非零的地址值
$8 = 0x804a030 ""
Continuing.
Breakpoint 7, main () at TestStructPoint.c:36
pstu->score = 99;
(gdb) p pstu->name
$9 = 0x804a030 "Jimy" &----pstu->name指向地址中的内容发生改变
Continuing.
Program exited normally.根据上面的调试可以知道,指针变量在定义过程中没有初始化为NULL,则指针变量指向的地址就是0x0,而在Linux中的进程虚拟存储器映射中地址0x0并没有映射,因此会出现错误。因此结构体中的指针变量一定要指向一块具体的存储空间之后才能进行相应的操作。同时其他的指针也必须指向相应的地址以后再操作。但是分配完地址后还需要在相应操作结束后释放分配的存储器,不然会造成浪费,以及内存的泄漏。这也是很多程序员忘记完成的工作。内存的释放采用free函数即可,free函数是将分配的这块内存与指针(malloc返回的指针)之间的所有关系斩断,指针变量P中存储的地址(这块内存的起始地址)值也没有发生变化,同时存储器中存储的内容也并没有发生改变,改变的只是指针对这块内存地址的所有权问题。但是该起始地址所在内存中的数据内容已经没法使用了,即时采用其他的指针也不能访问。如果下一次调用malloc函数可能会在刚才释放的区域创建一个内存空间,由于释放以后的存储空间的内容并没有改变(我是参考书上的,但我认为free后存储器中的内容是发生变化的,后面的调试可以说明这个问题,只是不知道发生什么变化,我也只是猜测,但是不要访问这个存储空间的内容是最安全的),这样可能会影响后面的结果,因此需要对创建的内存空间进行清零操作(防止前面的操作影响后面),这通常采用memset函数实现,具体参看memset函数。还有指针变量P中存储的地址值并没有改变,由于指针P没有对这个地址的访问权限,程序中对P的引用都可能导致错误的产生,造成野指针,因此最后还需要将指针P指向NULL,避免野指针的产生。当然也需要对创建是否成功需要检测,但这里我暂时不考虑这些错误的处理。#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct student
&&&&&&&&char *name;
&&&&&&&&int score;
}stu,*pstu;
int main()
&&&&&&&&/*为name分配指向的一段内存空间*/
&&&&&&&&stu.name = (char *)malloc(20*sizeof(char));
&&&&&&&&memset(stu.name,0,20*sizeof(char));
&&&&&&&&strcpy(stu.name,"Jimy");
&&&&&&&&stu.score = 99;
&&&&&&&&/*为pstu分配指向的一段内存空间*/
&&&&&&&&pstu = (struct student *)malloc(sizeof(struct student));
&&&&&&&&memset(pstu,0,sizeof(struct student));
&&&&&&&&/*为name分配指向的一段内存空间*/
&&&&&&&&pstu->name = (char *)malloc(20*sizeof(char));
&&&&&&&&memset(pstu->name,0,20*sizeof(char));
&&&&&&&&strcpy(pstu->name,"Jimy");
&&&&&&&&pstu->score = 99;
&&&&&&&&/*采用另外的指针访问分配的存储空间,测试内存中内容是否改变*/
&&&&&&&&char *p = stu.name;
&&&&&&&&char *p1 = (char *)0x804a008;//具体的地址值
&&&&&&&&char *ppstu = pstu->name;
&&&&&&&&char *pp = (char *)0x804a030;//具体的地址值
&&&&&&&&/*释放的顺序要注意,pstu->name必须在pstu释放之前释放,& & & & &*如果pstu先释放,那么pstu->name就不能正确的访问。
&&&&&&&&*/
&&&&&&&&free(pstu->name);
&&&&&&&&free(stu.name);
&&&&&&&&free(pstu);
&&&&&&&&/*为了防止野指针产生*/
&&&&&&&&pstu->name = NULL;
&&&&&&&&stu.name = NULL;
&&&&&&&&pstu = NULL;
&&&&&&&&p = NULL;
&&&&&&&&ppstu = NULL;
&&&&&&&&return 0;
}下面的全部是调试结果,根据调试结果说明问题:(gdb) rStarting program: /home/gong/program/cprogram/TestStructPoint&Breakpoint 1, main () at TestStructPoint.c:1414
stu.name = (char *)malloc(20*sizeof(char));Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686(gdb) p stu$1 = {name = 0x0, score = 0}(gdb) p stu.name$2 = 0x0(gdb) cContinuing.Breakpoint 2, main () at TestStructPoint.c:1717
strcpy(stu.name,"Jimy");(gdb) p stu.name$3 = 0x804a008 ""(gdb) cContinuing.Breakpoint 3, main () at TestStructPoint.c:2121
pstu = (struct student *)malloc(sizeof(struct student));(gdb) p stu.name$4 = 0x804a008 "Jimy"(gdb) p stu$5 = {name = 0x804a008 "Jimy", score = 99}(gdb) p pstu$6 = (struct student *) 0x0(gdb) cContinuing.Breakpoint 4, main () at TestStructPoint.c:2424
pstu->name = (char *)malloc(20*sizeof(char));(gdb) p pstu$7 = (struct student *) 0x804a020(gdb) p pstu->name$8 = 0x0(gdb) cContinuing.Breakpoint 5, main () at TestStructPoint.c:2727
strcpy(pstu->name,"Jimy");(gdb) p pstu->name$9 = 0x804a030 ""(gdb) cContinuing.Breakpoint 6, main () at TestStructPoint.c:3131
char *p = stu.(gdb) p pstu->name$10 = 0x804a030 "Jimy"(gdb) p *pstu$11 = {name = 0x804a030 "Jimy", score = 99}(gdb) p p$12 = 0x854ff4 "|M\205"(gdb) cContinuing.Breakpoint 7, main () at TestStructPoint.c:3232
char *p1 = (char *)0x804a008;//具体的地址值 (gdb) p p1$13 = 0x855ca0 ""(gdb) cContinuing.Breakpoint 8, main () at TestStructPoint.c:3333
char *ppstu = pstu->(gdb) p p1$14 = 0x804a008 "Jimy"(gdb) p ppstu$15 = 0x855ca0 ""(gdb) cContinuing.Breakpoint 9, main () at TestStructPoint.c:3434
char *pp = (char *)0x804a030;//具体的地址值(gdb) p ppstu$16 = 0x804a030 "Jimy"(gdb) p pp$17 = 0x804826a "__libc_start_main"(gdb) cContinuing.Breakpoint 10, main () at TestStructPoint.c:3737
free(pstu->name);(gdb) p pp$18 = 0x804a030 "Jimy"(gdb) p pstu->name$19 = 0x804a030 "Jimy"(gdb) cContinuing.Breakpoint 11, main () at TestStructPoint.c:3838
free(stu.name);(gdb) p pstu->name$20 = 0x804a030 ""(gdb) cContinuing.Breakpoint 12, main () at TestStructPoint.c:3939
free(pstu);(gdb) p stu.name$21 = 0x804a008 "(\240\004\b"(gdb) p pstu$22 = (struct student *) 0x804a020(gdb) p *pstu$23 = {name = 0x804a030 "", score = 99}(gdb) cContinuing.Breakpoint 13, main () at TestStructPoint.c:4141
pstu->name = NULL;(gdb) p *pstu$24 = {name = 0x0, score = 99}(gdb) p pstu->name$25 = 0x0(gdb) cContinuing.Breakpoint 14, main () at TestStructPoint.c:4747
return 0;(gdb) p p1$26 = 0x804a008 "(\240\004\b"(gdb) p pp$27 = 0x804a030 ""(gdb) p pstu$28 = (struct student *) 0x0(gdb) p pstu->nameCannot access memory at address 0x0(gdb)&具体的调试过程说明了其中很多的问题,根据其中的结果可以知道,free结束以后指针变量P(malloc返回)中存储的地址值并没有改变,但是通过该地值已经不能访问之前的分配的存储空间。我采用其他的指针(直接赋值地址以及指针赋值)访问得到的结果也不是正确的值,虽然这不能说明地址中的数据改变了,但说明对释放以后的存储空间再通过其他方法访问不会得到正确的值,但是内存空间中存在值,并不一定是0,因此每一次malloc都清零是必要的。防止野指针也是非常必要的,减少程序错误的概率。最后说明一下,链表作为结构体的衍生产物,链表的结构体中就有指针变量,因此一定草采用malloc等分配好内存块以后,再对链表进行操作,不然都会导致不同问题的产生。
阅读(10203) | 评论(0) | 转发(4) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。developerWorks 社区
本文将带您了解一些良好的和内存相关的编码实践,以将内存错误保持在控制范围内。内存错误是 C 和 C++ 编程的祸根:它们很普遍,认识其严重性已有二十多年,但始终没有彻底解决,它们可能严重影响应用程序,并且很少有开发团队对其制定明确的管理计划。但好消息是,它们并不怎么神秘。
(), 副总裁, Phaseit Inc.
Cameron Laird 是 developerWorks 长期投稿者和前专栏作家。他经常编写关于促进其公司应用程序开发的开源项目的文章,主要关注可靠性和安全性。
引言C 和 C++ 程序中的内存错误非常有害:它们很常见,并且可能导致严重的后果。来自计算机应急响应小组(请参见)和供应商的许多最严重的安全公告都是由简单的内存错误造成的。自从 70 年代末期以来,C 程序员就一直讨论此类错误,但其影响在 2007 年仍然很大。更糟的是,如果按我的思路考虑,当今的许多 C 和 C++ 程序员可能都会认为内存错误是不可控制而又神秘的顽症,它们只能纠正,无法预防。但事实并非如此。本文将让您在短时间内理解与良好内存相关的编码的所有本质:正确的内存管理的重要性存在内存错误的 C 和 C++ 程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。从 1988 年著名的莫里斯蠕虫 攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关:“大多数计算机安全漏洞都是缓冲区溢出”,Rodney Bates 在 2004 年写道。在可以使用 C 或 C++ 的地方,也广泛支持使用其他许多通用语言(如 Java&#8482;、Ruby、Haskell、C#、Perl、Smalltalk 等),每种语言都有众多的爱好者和各自的优点。但是,从计算角度来看,每种编程语言优于 C 或 C++ 的主要优点都与便于内存管理密切相关。与内存相关的编程是如此重要,而在实践中正确应用又是如此困难,以致于它支配着面向对象编程语言、功能性编程语言、高级编程语言、声明性编程语言和另外一些编程语言的所有其他变量或理论。与少数其他类型的常见错误一样,内存错误还是一种隐性危害:它们很难再现,症状通常不能在相应的源代码中找到。例如,无论何时何地发生内存泄漏,都可能表现为应用程序完全无法接受,同时内存泄漏不是显而易见。因此,出于所有这些原因,需要特别关注 C 和 C++ 编程的内存问题。让我们看一看如何解决这些问题,先不谈是哪种语言。内存错误的类别首先,不要失去信心。有很多办法可以对付内存问题。我们先列出所有可能存在的实际问题:内存泄漏错误分配,包括大量增加 free() 释放的内存和未初始化的引用悬空指针数组边界违规这是所有类型。即使迁移到 C++ 面向对象的语言,这些类型也不会有明显变化;无论数据是简单类型还是 C 语言的 struct 或 C++ 的类,C 和 C++ 中内存管理和引用的模型在原理上都是相同的。以下内容绝大部分是“纯 C”语言,对于扩展到 C++ 主要留作练习使用。内存泄漏在分配资源时会发生内存泄漏,但是它从不回收。下面是一个可能出错的模型(请参见):清单 1. 简单的潜在堆内存丢失和缓冲区覆盖 void f1(char *explanation)
p1 = malloc(100);
(void) sprintf(p1,
"The f1 error occurred because of '%s'.",
explanation);
local_log(p1);
}您看到问题了吗?除非 local_log() 对 free() 释放的内存具有不寻常的响应能力,否则每次对 f1 的调用都会泄漏 100 字节。在记忆棒增量分发数兆字节内存时,一次泄漏是微不足道的,但是连续操作数小时后,即使如此小的泄漏也会削弱应用程序。在实际的 C 和 C++ 编程中,这不足以影响您对 malloc() 或 new 的使用,本部分开头的句子提到了“资源”不是仅指“内存”,因为还有类似以下内容的示例(请参见)。FILE 句柄可能与内存块不同,但是必须对它们给予同等关注:清单 2. 来自资源错误管理的潜在堆内存丢失 int getkey(char *filename)
fp = fopen(filename, "r");
fscanf(fp, "%d", &key);
}fopen 的语义需要补充性的 fclose。在没有 fclose() 的情况下,C 标准不能指定发生的情况时,很可能是内存泄漏。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑。内存错误分配错误分配的管理不是很困难。下面是一个示例(请参见):清单 3. 未初始化的指针 void f2(int datum)
No one has initialized p2. */
}关于此类错误的好消息是,它们一般具有显著结果。在 AIX&#174; 下,对未初始化指针的分配通常会立即导致 segmentation fault 错误。它的好处是任何此类错误都会被快速地检测到;与花费数月时间才能确定且难以再现的错误相比,检测此类错误的代价要小得多。在此错误类型中存在多个变种。free() 释放的内存比 malloc() 更频繁(请参见):清单 4. 两个错误的内存释放 /* Allocate once, free twice. */
p = malloc(10);
/* Allocate zero times, free once. */
/* Note that p remains uninitialized here. */
}这些错误通常也不太严重。尽管 C 标准在这些情形中没有定义具体行为,但典型的实现将忽略错误,或者快速而明确地对它们进行标记;总之,这些都是安全情形。悬空指针悬空指针比较棘手。当程序员在内存资源释放后使用资源时会发生悬空指针(请参见):清单 5. 悬空指针
struct x *
xp = (struct x *) malloc(sizeof (struct x));
xp.q = 13;
/* Problem!
There's no guarantee that
the memory block to which xp points
hasn't been overwritten. */
return xp.q;
}传统的“调试”难以隔离悬空指针。由于下面两个明显原因,它们很难再现:即使影响提前释放内存范围的代码已本地化,内存的使用仍然可能取决于应用程序甚至(在极端情况下)不同进程中的其他执行位置。悬空指针可能发生在以微妙方式使用内存的代码中。结果是,即使内存在释放后立即被覆盖,并且新指向的值不同于预期值,也很难识别出新值是错误值。悬空指针不断威胁着 C 或 C++ 程序的运行状态。数组边界违规数组边界违规十分危险,它是内存错误管理的最后一个主要类别。回头看一下;如果 explanation 的长度超过 80,则会发生什么情况?回答:难以预料,但是它可能与良好情形相差甚远。特别是,C 复制一个字符串,该字符串不适于为它分配的 100 个字符。在任何常规实现中,“超过的”字符会覆盖内存中的其他数据。内存中数据分配的布局非常复杂并且难以再现,所以任何症状都不可能追溯到源代码级别的具体错误。这些错误通常会导致数百万美元的损失。内存编程的策略勤奋和自律可以让这些错误造成的影响降至最低限度。下面我们介绍一下您可以采用的几个特定步骤;我在各种组织中处理它们的经验是,至少可以按一定的数量级持续减少内存错误。编码风格编码风格是最重要的,我还从没有看到过其他任何作者对此加以强调。影响资源(特别是内存)的函数和方法需要显式地解释本身。下面是有关标头、注释或名称的一些示例(请参见)。清单 6. 识别资源的源代码示例 /********
* Note that any function invoking protected_file_read()
* assumes responsibility eventually to fclose() its
* return value, UNLESS that value is NULL.
FILE *protected_file_read(char *filename)
fp = fopen(filename, "r");
* Note that the return value of get_message points to a
* fixed memory location.
Do NOT free() remember to
* make a copy if it must be retained ...
char *get_message()
static char this_buffer[400];
(void) sprintf(this_buffer, ...);
return this_
* While this function uses heap memory, and so
* temporarily might expand the over-all memory
* footprint, it properly cleans up after itself.
int f6(char *item1)
my_class c1;
c1 = new my_class(item1);
result = c1.x;
delete c1;
* Note that f8() is documented to return a value
* which needs to as f7 thinly
* wraps f8, any code which invokes f7() must be
* careful to free() the return value.
p = f8(...);
}使这些格式元素成为您日常工作的一部分。可以使用各种方法解决内存问题:专用库语言软件工具硬件检查器在这整个领域中,我始终认为最有用并且投资回报率最大的是考虑改进源代码的风格。它不需要昂贵的代价或严格的形式;可以始终取消与内存无关的段的注释,但影响内存的定义当然需要显式注释。添加几个简单的单词可使内存结果更清楚,并且内存编程会得到改进。我没有做受控实验来验证此风格的效果。如果您的经历与我一样,您将发现没有说明资源影响的策略简直无法忍受。这样做很简单,但带来的好处太多了。检测检测是编码标准的补充。二者各有裨益,但结合使用效果特别好。机灵的 C 或 C++ 专业人员甚至可以浏览不熟悉的源代码,并以极低的成本检测内存问题。通过少量的实践和适当的文本搜索,您能够快速验证平衡的 *alloc() 和 free() 或者 new 和 delete 的源主体。人工查看此类内容通常会出现像 中一样的问题。清单 7.
棘手的内存泄漏 static char *important_pointer = NULL;
if (!important_pointer)
important_pointer = malloc(IMPORTANT_SIZE);
if (condition)
We just lost the reference
important_pointer already held. */
important_pointer = malloc(DIFFERENT_SIZE);
}如果 condition 为真,简单使用自动运行时工具不能检测发生的内存泄漏。仔细进行源分析可以从此类条件推理出证实正确的结论。我重复一下我写的关于风格的内容:尽管大量发布的内存问题描述都强调工具和语言,对于我来说,最大的收获来自“软的”以开发人员为中心的流程变更。您在风格和检测上所做的任何改进都可以帮助您理解由自动化工具产生的诊断。静态的自动语法分析当然,并不是只有人类才能读取源代码。您还应使静态语法分析 成为开发流程的一部分。静态语法分析是 lint、严格编译 和几种商业产品执行的内容:扫描编译器接受的源文本和目标项,但这可能是错误的症状。希望让您的代码无 lint。尽管 lint 已过时,并有一定的局限性,但是,没有使用它(或其较高级的后代)的许多程序员犯了很大的错误。通常情况下,您能够编写忽略 lint 的优秀的专业质量代码,但努力这样做的结果通常会发生重大错误。其中一些错误影响内存的正确性。与让客户首先发现内存错误的代价相比,即使对这种类别的产品支付最昂贵的许可费也失去了意义。清除源代码。现在,即使 lint 标记的编码可能向您提供所需的功能,但很可能存在更简单的方法,该方法可满足 lint,并且比较强键又可移植。内存库补救方法的最后两个类别与前三个明显不同。前者是轻量级 的;一个人可以容易地理解并实现它们。另一方面,内存库和工具通常具有较高的许可费用,对部分开发人员来说,它们需要进一步完善和调整。有效地使用库和工具的程序员是理解轻量级的静态 方法的人员。可用的库和工具给人的印象很深:其作为组的质量很高。但是,即使最优秀的编程人员也可能会被忽略内存管理基本原则的非常任性的编程人员搅乱。据我观察,普通的编程人员在尝试利用内存库和工具进行隔离工作时也只能感到灰心。由于这些原因,我们催促 C 和 C++ 程序员为解决内存问题先了解一下自己的源。在这完成之后,才去考虑库。使用几个库能够编写常规的 C 或 C++ 代码,并保证改进内存管理。Jonathan Bartlett 在 developerWorks 的 2004 评论专栏中介绍了主要的候选项,可以在下面的部分获得。库可以解决多种不同的内存问题,以致于直接对它们进行比较是非常困难的;这方面的常见主题包括垃圾收集、智能指针 和 智能容器。大体上说,库可以自动进行较多的内存管理,这样程序员可以犯更少的错误。我对内存库有各种感受。他们在努力工作,但我看到他们在项目中获得的成功比预期要小,尤其在 C 方面。我尚未对这些令人失望的结果进行仔细分析。例如,业绩应该与相应的手动 内存管理一样好,但是这是一个灰色区域——尤其在垃圾收集库处理速度缓慢的情况下。通过这方面的实践得出的最明确的结论是,与 C 关注的代码组相比,C++ 似乎可以较好地接受智能指针。内存工具开发真正基于 C 的应用程序的开发团队需要运行时内存工具作为其开发策略的一部分。已介绍的技术很有价值,而且不可或缺。在您亲自尝试使用内存工具之前,其质量和功能您可能还不了解。本文主要讨论了基于软件的内存工具。还有硬件内存调试器;在非常特殊的情况下(主要是在使用不支持其他工具的专用主机时)才考虑它们。市场上的软件内存工具包括专有工具(如 IBM Rational&#174; Purify 和 Electric Fence)和其他开放源代码工具。其中有许多可以很好地与 AIX 和其他操作系统一起使用。所有内存工具的功能基本相同:构建可执行文件的特定版本(很像在编译时通过使用 -g 标记生成的调试版本)、练习相关应用程序和研究由工具自动生成的报告。请考虑如 所示的程序。清单 8. 示例错误 int main()
char p[5];
strcpy(p, "Hello, world.");
}此程序可以在许多环境中“运行”,它编译、执行并将“Hello, world.\n”打印到屏幕。使用内存工具运行相同应用程序会在第四行产生一个数组边界违规的报告。在了解软件错误(将十四个字符复制到了只能容纳五个字符的空间中)方面,这种方法比在客户处查找错误症状的花费小得多。这是内存工具的功劳。结束语作为一名成熟的 C 或 C++ 程序员,您认识到内存问题值得特别关注。通过制订一些计划和实践,可以找到控制内存错误的方法。学习内存使用的正确模式,快速发现可能发生的错误,使本文介绍的技术成为您日常工作的一部分。您可以在开始时就消除应用程序中的症状,否则可能要花费数天或数周时间来调试。
参考资料 您可以参阅本文在 developerWorks 全球站点上的
。:计算机应急响应小组是“联邦政府资助的研发中心”,通过该中心以及许多其他活动发布关于
的技术计算机安全警报。:这是助理教授 Rodney Bates 为 ACM Queue 撰写的一篇关于 C 编程和缓冲区溢出的文章。""(developerWorks,2004 年 11 月):概述了对 Linux&#174; 程序员有用的内存管理技术,主要适用于 C 语言,但是也适用于其他语言。:学习更多关于此主要专有内存工具的内容。:此站点提供产品和服务,而且与 C 和 C++ 的静态源代码分析有关。:这是我在 2004 年撰写的一篇文章。我还维护
的网页。:AIX and UNIX developerWorks专区提供了大量与 AIX 系统管理的所有方面相关并扩展您的 UNIX 技能的信息。:访问 New to AIX and UNIX 页面以了解更多关于 AIX 和 UNIX 的内容。:AIX 相关技术信息的协作环境。查看 Cameron Laird 撰写的其他文章和教程。
按主题搜索“AIX and UNIX”库:
:访问这个电子参考库以查找特定的技术资源。
:了解最新的 developerWorks 技术事件和网络广播。:收听 Podcast 并与 IBM 技术专家保持同步。:使用 IBM 试用软件开发您的下一个项目,可直接从 developerWorks 下载这些试用软件。参与 ,从而加入到 developerWorks 社区中来。参与“AIX and UNIX”论坛:
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
免费下载、试用软件产品,构建应用并提升技能。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=AIX and UNIXArticleID=232583ArticleTitle=内存调试技巧publish-date=

我要回帖

更多关于 临时电调试常见问题 的文章

 

随机推荐