linux下编程遇到的一个疑难杂症——野指针
- 格式:doc
- 大小:35.50 KB
- 文档页数:2
Linux下的段错误(Segmentation fault )产生的原因及调试方法(经典)2009-04-05 11:25简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址.一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。
一旦一个程序发生了越界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了•在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的1)访问系统数据区,尤其是往系统保护的内存地址写数据最常见就是给一个指针以0地址2)内存越界(数组越界,变量类型不一致等)访问到不属于你的内存区域解决方法我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。
实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。
但是手工除虫”(debug),往往是效率低下且让人厌烦的,本文将就”段错误”这个内存访问越界的错误谈谈如何快速定位这些"段错误”的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:1 dummy_function (void)2 {3 unsigned char *ptr = 0x00;4 *ptr = 0x00;5 }67 int main (void)8 {9 dummy_function ();1011 return 0;12 }作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0 的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。
【⾯试题】野指针的成因,危害以及避免⽅法?概念:野指针指向了⼀块随机内存空间,不受程序控制。
如指针指向已经被删除的对象或者指向⼀块没有访问权限的内存空间,之后如果对其再解引⽤的话,就会出现问题。
野指针产⽣的原因:1、指针定义时未被初始化:指针在被定义的时候,如果程序不对其进⾏初始化的话,它会指向随机区域,因为任何指针变量(除了static修饰的指针变量)在被定义的时候是不会被置空的,它的默认值是随机的。
2、指针被释放时没有被置空:我们在⽤malloc开辟内存空间时,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的⾸地址。
指针指向的内存空间在⽤free()或者delete(注意delete只是⼀个操作符,⽽free()是⼀个函数)释放后,如果程序员没有对其置空或者其他的赋值操作,就会使其成为⼀个野指针。
3、指针操作超越变量作⽤域:不要返回指向栈内存的指针或引⽤,因为栈内存在函数结束的时候会被释放,⽰例(转⾃⾼质量C++):class A{public:void Func(void){cout << “Func of class A” << endl;}};class B{public:A *p;void Test(void){A a;p = &a; // 注意 a 的⽣命期,只在这个函数Test中,⽽不是整个class B}void Test1(){p->Func(); // p 是“野指针”}};函数 Test1 在执⾏语句 p->Func()时,p 的值还是 a 的地址,对象 a 的内容已经被清除,所以 p 就成了“野指针” 。
野指针的危害:野指针的问题在于,指针指向的内存已经⽆效了,⽽指针没有被置空,解引⽤⼀个⾮空的⽆效指针是⼀个未被定义的⾏为,也就是说不⼀定导致段错误,野指针很难定位到是哪⾥出现的问题,在哪⾥这个指针就失效了,不好查找出错的原因。
初学者必读Linux开发常见错误及解决方法在Linux开发的过程中,初学者经常会遇到各种错误和问题。
本文将介绍一些常见的错误,并提供相应的解决方法,帮助初学者更好地应对各种挑战。
一、命令不存在或找不到的错误在使用Linux命令时,有时会出现“Command not found”或“No command found”的错误提示。
这通常是由于命令不存在或命令所在的目录不在系统的PATH环境变量中引起的。
解决方法:1. 检查命令是否存在于系统中,可以使用命令“which 命令名”或“whereis 命令名”来查找命令的路径。
2. 如果命令存在但不在系统的PATH环境变量中,可以通过编辑~/.bashrc文件或/etc/profile文件,在其中添加命令的路径即可。
3. 更新环境变量配置后,可以使用“source ~/.bashrc”或“source /etc/profile”命令来使配置生效。
二、文件权限错误当在Linux中执行某个程序或访问某个文件时,有时会出现“Permission denied”或“Access denied”的错误提示。
这通常是由于当前用户对该文件或目录没有足够的权限引起的。
解决方法:1. 使用“ls -l”命令查看文件或目录的权限设置,确保当前用户具有读、写、执行权限。
2. 如果当前用户没有足够的权限,可以使用“chmod”命令来修改文件或目录的权限。
例如,使用“chmod +x 文件名”为文件添加执行权限,使用“chmod 777 目录名”给目录赋予所有权限。
3. 如果需要访问的文件或目录所属于其他用户,可以使用“sudo”命令以超级用户的身份执行相关操作。
三、编译错误在进行Linux下的编程开发时,有时会遇到编译错误,如“undefined reference”、“syntax error”等。
这通常是由于代码中存在语法错误或库文件引用不正确引起的。
解决方法:1. 仔细检查代码中的语法错误,特别是标点符号、括号匹配等方面。
野指针问题野指针,也就是指向不可用内存区域的指针。
通常对这种指针进行操作的话,将会使程序发生不可预知的错误。
“野指针”不是NULL指针,是指向“垃圾”内存的指针。
人们一般不会错用NULL指针,因为用if语句很容易判断。
但是“野指针”是很危险的,if语句对它不起作用。
野指针的成因主要有两种:(1)、指针变量没有被初始化。
任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。
所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
(2)、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。
通常会用语句if (p != NULL)进行防错处理。
很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。
例:char *p = (char *) malloc(100);strcpy(p, “hello”);free(p); // p 所指的内存被释放,但是p所指的地址仍然不变if(p != NULL) // 没有起到防错作用strcpy(p, “world”); // 出错另外一个要注意的问题:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
首先请诸位看以下一段“危险”的C++代码:void function( void ){char* str = new char[100];delete[] str;// Do somethingstrcpy( str, "Dangerous!!" );}之所以说其危险,是因为这是一段完全合乎语法的代码,编译的时候完美得一点错误也不会有,然而当运行到strcpy一句的时候,问题就会出现,因为在这之前,str的空间已经被delete掉了,所以strcpy当然不会成功。
初学者必读Linux开发常见错误及解决方法Linux开发作为一种广泛应用的开发环境,吸引着越来越多的开发者。
然而,对于初学者来说,遇到各种错误是难以避免的。
本文将介绍一些初学者常见的Linux开发错误,并提供相应的解决方法,以帮助读者更好地掌握Linux开发技巧。
一、错误一:拼写错误在Linux开发中,即使是细微的拼写错误也可能导致程序无法正常运行。
初学者们应该养成查看代码拼写的习惯,确保代码中的字符与关键字完全匹配。
解决方法:1. 仔细检查代码,确保符号、函数名和变量名的拼写是正确的。
2. 通过代码编辑器和集成开发环境(IDE)的自动补全功能来减少拼写错误的可能性。
二、错误二:文件权限问题Linux系统中,文件权限是非常重要的。
如果文件没有正确的权限设置,可能会导致程序无法运行或无法访问所需的文件或目录。
解决方法:1. 使用chmod命令改变文件的权限,确保执行权限被正确设置。
2. 在使用敏感文件或目录时,确保相应的读写权限已经授予。
3. 不要使用超级用户账号(root)执行程序,而是使用普通用户账号运行程序。
三、错误三:软件依赖问题在Linux开发中,许多软件和库是相互依赖的。
如果缺少依赖的软件包或库,可能会导致编译错误或运行时错误。
解决方法:1. 使用包管理器(如apt、yum等)来安装缺少的软件包和库。
2. 确保软件版本与所需的依赖版本兼容。
四、错误四:编译错误编译错误是初学者常遇到的问题之一。
常见的编译错误包括语法错误、函数未声明等。
解决方法:1. 仔细阅读错误信息,确定错误的类型和位置。
2. 在编写代码时,注意代码的语法和规范,避免低级错误。
3. 参考相关文档和教程,查找解决方法。
五、错误五:内存管理问题Linux开发中,内存管理是一个重要的方面。
如何正确地申请和释放内存是初学者需要注意的问题。
解决方法:1. 在申请内存之后,确保适当地释放内存,避免内存泄漏。
2. 使用动态内存分配函数(如malloc、free等)时,务必谨慎操作。
寻找“野指针”本文介绍了一种在调试过程中寻找悬挂指针(野指针)的方法,这种方法是通过对new和delete运算符的重载来实现的。
这种方法不是完美的,它是以调试期的内存泄露为代价来实现的,因为文中出现的代码是绝不能出现在一个最终发布的软件产品中的,只能在调试时使用。
在VC中,在调试环境下,可以简单的通过把new替换成DEBUG_NEW来实现功能更强更方便的指针检测,详情可参考MSDN。
DEBUG_NEW的实现思路与本文有相通的地方,因此文章中介绍的方法虽然不是最佳的,但还算实用,更重要的是,它提供给我们一种新的思路。
简介:前几天发生了这样一件事,我正在调试一个程序,这个程序用了一大堆乱七八糟的指针来处理一个链表,最终在一个指向链表结点的指针上出了问题。
我们预计它应当指向的是一个虚基类的对象。
我想到第一个问题是:指针所指的地方真的有一个对象吗?出问题的指针值可以被4整除,并且不是NULL的,所以可以断定它曾经是一个有效的指针。
通过使用Visual Studio的内存查看窗口(View->Debug Windows->Memory)我们发现这个指针所指的数据是FE EE FE EE FE EE ...这通常意味着内存是曾经是被分配了的,但现在却处于一种未分配的状态。
不知是谁、在什么地方把我的指针所指的内存区域给释放掉了。
我想要找出一种方案来查出我的数据到底是怎么会被释放的。
背景:我最终通过重载了new和delete运算符找到了我丢失的数据。
当一个函数被调用时,参数会首先被压到栈上后,然后返回地址也会被压到栈上。
我们可以在new和delete运算符的函数中把这些信息从栈上提取出来,帮助我们调试程序。
代码:在经历了几次错误的猜测后,我决定求助于重载new和delete运算符来帮我找到我的指针所指向的数据。
下面的new运算符的实现把返回地址从栈上提了出来。
这个返回地址位于传递过来的参数和第一个局部变量的地址之间。
C语⾔指针详解之野指针⽬录指针是什么?怎么表⽰?什么是指针变量?指针类型⼜是什么?指针类型存在的意义野指针是什么?野指针产⽣的原因⼀、指针未初始化⼆、指针越界访问如何避免野指针(野狗)的出现呢?指针运算总结指针是什么?指针只是⼀个名词⽽已,指针就是地址。
我们平时说指针,也可以指指针变量。
怎么表⽰?类型名指针变量 = 地址;例如:int* pa = &a;//我们这⾥的指针类型叫做int*,我读做(yin te 星号)。
//pa是指针变量这段表达式的意思是:定义了⼀个指针变量pa,⾥⾯存放的是a的地址。
⽽这个指针变量的类型为int*。
那下⾯就有同学疑惑了,那什么是指针变量?什么是指针变量?很简单,在之前我们学习了怎么定义整型变量a。
⽐如定义⼀个《整型》《变量a》,然后把a初始化为10。
int a = 10;不过现在变了,我们现在学习了指针。
可以定义⼀个《int*》《变量pa》,然后把pa初识化为&a。
注意:int* 是⼀个类型。
叫做指针类型pa就叫做指针变量int* pa = &a;指针类型⼜是什么?既然变量有不同的类型,⽐如整型,浮点型等。
那么指针也有也有不同的类型。
char *pc = NULL;int *pi = NULL;short *ps = NULL;long *pl = NULL;float *pf = NULL;double *pd = NULL;//NULL为空指针。
这⾥可以看到,指针的定义⽅式是:类型 + * 。
其实:char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
指针类型存在的意义那有这么多的指针的类型,指针类型的意义是什么?我们在这⾥先说两个重要结论:指针的类型决定了指针向前或者向后⾛⼀步(也就是地址+1)有多⼤(能⾛多少个字节)指针的类型决定了,对指针解引⽤的时候有多⼤的权限(能操作⼏个字节)。
什么是野指针和段错误1、野指针与段错误问题1.1、什么是野指针?所谓野指针就是,指针指向一个不确定的地址空间,或者指向的是一个确定的地址空间的,但引用空间的结果却是不可预知的,这样的指针就称为野指针。
例子1:int main(void) {int *p;*p = 10;return 0;}本例子中,p是自动局部变量,由于p没有被初始化,也没有被后续赋值,那么p中存放的是一个随机值,所以p指向的内存空间是不确定的,访问一个不确定的地址空间,结果显然是不可预知的。
例子1:int main(void) {int *p=0x13542354;*p = 10;return 0;}本例子中,p虽然是指向了一个确定地址“0x43542354”的空间,但是它对应的空间是否存在,其读写权限是否满足程序的访问要求,都是未知数,所以导致的结果也是未知的。
通过以上两个例子了解到,在给指针变量绑定地址,指向某个空间时,一定要是确定的,不能出现不可预知性,一旦出现未知性它就是一个野指针,即使某一次没有产生严重后果,但埋下了这颗地雷后,就留下了不可预知的隐患,对于程序来说这是不可接受的。
1.2、野指针可能引发的危害(1)引发段错误段错误就是地址错误,其实是一种对程序和系统的保护性措施,一旦产生段错误,程序会立即终止,防止错误循环叠加,产生雪崩式的错误。
(2)未产生任何结果有的时候,可能使用了一个野指针,虽然指向了一个未知的地址空间,但是这空间可以使用,而且该空间和程序中的其它变量空间没有交集,对野指针指向的空间进行了读写访问后,也不会对程序产生任何影响,虽然如此,这种野指针也是必须要极力避免的,因为这个隐患很可能在后面的程序运行中,导致严重的错误,而且这种错误很难排查。
(3)引发程序连环式错误访问野指针产生的错误循环叠加,轻者程序结果严重扭曲,重者直接导致程序或者系统崩溃。
1.3、野指针产生的原因野指针产生的原因大概有如下三种:(1)在使用指针前,忘记了给指针变量初始化或者赋值一个有效的空间地址,导致指向的不确定。
野(wild)指针与悬空(dangling)指针1. 什么是野指针(wild pointer)?A pointer in c which has not been initialized is known as wild pointer.野指针(wild pointer)就是没有被初始化过的指针。
例如,o foo1.c1int main(int argc, char *argv[])2 {3int *p;4return (*p & 0x7f); /* XXX: p is a wild pointer */5 }如果⽤"gcc -Wall"编译, 会出现如下警告:1 $ gcc -Wall -g -m32 -o foo foo.c2 foo.c: In function ‘main’:3 foo.c:4:10: warning: ‘p’ is used uninitialized in this function [-Wuninitialized]4 return (*p & 0x7f); /* XXX: p is a wild pointer */5 ^2. 什么是悬空指针(dangling pointer)?If a pointer still references the original memory after it has been freed, it is called a dangling pointer.悬空指针是指针最初指向的内存已经被释放了的⼀种指针。
典型的悬空指针看起来是这样的,(图⽚来源是)如果两个指针(p1和p2)指向同⼀块内存区域, 那么free(p1)后,p1和p2都成为悬空指针。
如果进⼀步将p1设置为NULL, 那么p2还是悬空指针。
诚然,使⽤*p1会导致⾮法内存访问,但是使⽤*p2却会出现⽆法预料的结果,可谓防不胜防。
例如:o foo2.c1 #include <stdlib.h>2 int main(int argc, char *argv[])3 {4 int *p1 = (int *)malloc(sizeof (int));5 int *p2 = p1; /* p2 and p1 are pointing to the same memory */6 free(p1); /* p1 is a dangling pointer, so is p2 */7 p1 = NULL; /* p1 is not a dangling pointer any more */8 return (*p2 & 0x7f); /* p2 is still a dangling pointer */9 }3. 使⽤野指针和悬空指针的危害⽆论是野指针还是悬空指针,都是指向⽆效内存区域(这⾥的⽆效指的是"不安全不可控")的指针。
linux下编程遇到的一个疑难杂症——野指针所带来的害处
分类:LINUX网络编程2012-04-12 14:15 653人阅读评论(0) 收藏举报场景:
一段代码,单进程多线程模式,除了main线程还有很多个子线程,里面大量地使用了指针,代码编译没有任何的warning和error;程序跑起来后一切正常,并和另外一个程序通过socket 网络通信,也一切正常;所有的子线程退出也正常;所有类的析构函数析构的时候也正常。
但是:就在main进程销毁(或者通过exit(0))退出的时候,操作系统就
提示以下错误:
*** glibc detected *** ./fsvspsiu: corrupted double-linked list: 0x08484100 ***
======= Backtrace: =========
/lib/libc.so.6[0x3bce1b]
/lib/libc.so.6[0x3be7fb]
/lib/libc.so.6(cfree+0x90)[0x3c20f0]
./fsvspsiu[0x804d4c5]
./fsvspsiu[0x804c411]
./fsvspsiu[0x804a134]
/lib/libc.so.6(exit+0xee)[0x38163e]
/lib/libc.so.6(__libc_start_main+0xe8)[0x36b398]
./fsvspsiu(__gxx_personality_v0+0x69)[0x8049211]
======= Memory map: ========
00110000-00111000 r-xp 00110000 00:00 0 [vdso]
00336000-00351000 r-xp 00000000 fd:00 332652 /lib/ld-2.7.so
00351000-00352000 r-xp 0001a000 fd:00 332652 /lib/ld-2.7.so
00352000-00353000 rwxp 0001b000 fd:00 332652 /lib/ld-2.7.so
00355000-004a8000 r-xp 00000000 fd:00 332653 /lib/libc-2.7.so
004a8000-004aa000 r-xp 00153000 fd:00 332653 /lib/libc-2.7.so
004aa000-004ab000 rwxp 00155000 fd:00 332653 /lib/libc-2.7.so
004ab000-004ae000 rwxp 004ab000 00:00 0
004b0000-004d7000 r-xp 00000000 fd:00 332657 /lib/libm-2.7.so
004d7000-004d8000 r-xp 00026000 fd:00 332657 /lib/libm-2.7.so
004d8000-004d9000 rwxp 00027000 fd:00 332657 /lib/libm-2.7.so
004e2000-004f7000 r-xp 00000000 fd:00 332655 /lib/libpthread-2.7.so
004f7000-004f8000 r-xp 00014000 fd:00 332655 /lib/libpthread-2.7.so
004f8000-004f9000 rwxp 00015000 fd:00 332655 /lib/libpthread-2.7.so
004f9000-004fb000 rwxp 004f9000 00:00 0
00cab000-00cb6000 r-xp 00000000 fd:00 332676 /lib/libgcc_s-4.1.2-20070925.so.1
00cb6000-00cb7000 rwxp 0000a000 fd:00 332676 /lib/libgcc_s-4.1.2-20070925.so.1
03694000-03774000 r-xp 00000000 fd:00 415472 /usr/lib/libstdc++.so.6.0.8
03774000-03778000 r-xp 000df000 fd:00 415472 /usr/lib/libstdc++.so.6.0.8
03778000-03779000 rwxp 000e3000 fd:00 415472 /usr/lib/libstdc++.so.6.0.8
03779000-0377f000 rwxp 03779000 00:00 0
08048000-08052000 r-xp 00000000 00:14 85112 /mnt/hgfs/d/fsvsp/src/pf/siu/fsvspsiu
08052000-08053000 rw-p 00009000 00:14 85112 /mnt/hgfs/d/fsvsp/src/pf/siu/fsvspsiu
08053000-08057000 rw-p 08053000 00:00 0
08452000-08486000 rw-p 08452000 00:00 0
b5600000-b5621000 rw-p b5600000 00:00 0
b5621000-b5700000 ---p b5621000 00:00 0
b5718000-b5719000 ---p b5718000 00:00 0
b5719000-b6119000 rw-p b5719000 00:00 0
b6119000-b611a000 ---p b6119000 00:00 0
b611a000-b6b1a000 rw-p b611a000 00:00 0
b6b1a000-b6b1b000 ---p b6b1a000 00:00 0
b6b1b000-b751b000 rw-p b6b1b000 00:00 0
b751b000-b751c000 ---p b751b000 00:00 0
b751c000-b7f1f000 rw-p b751c000 00:00 0
bfe86000-bfe9b000 rw-p bffea000 00:00 0 [stack]
Aborted
1. 通过一翻周折,终于找到了问题的根源:
看上面操作系统提示信息的第五行( /lib/libc.so.6(cfree+0x90)[0x3c20f0] ),可以估计应该是堆内存被free的时候出了问题,那么free()函数最有可能会出现问题的情况是什么呢
——堆被多次释放了。
2. 通过代码审核,发现:
有个malloc 出来的指针,不小心被free了,但是没有被赋值成NULL,变成野指针,下次使用的时候该野指针(已经被free)不等于NULL,所以使用该指针,使用完后又free了一次,造成了free两次的情况。
3. 还没有弄懂的问题:
操作系统为什么不在第二次free的时候报错,而是在整个进程退出的时候报错,太不厚道了。