当前位置:C++技术网 > 资讯 > 为什么栈中的数组越界后经常不崩溃,还可以正常的操作呢

为什么栈中的数组越界后经常不崩溃,还可以正常的操作呢

更新时间:2015-06-25 21:25:40浏览次数:1+次

    使用数组也是家常便饭,但是经常出现越界使用数组也能编译通过的奇怪问题,和我们书中学习的很不一样,怎么折腾也不知道为什么,郁闷了吧!哈哈哈,别慌,这里就给你答案啦。
    函数中分配的数组,不是使用动态分配的那就就是自动分配的,就是在线程栈的内存空间分配的。如果使用动态分配,即使用malloc或者new等来分配的,则是在堆中分配的。堆是是一块单独的内存块,供各个进程线程来使用的。而在栈中分配的内存,会自动分配,使用完后自动释放。我们从来都是定义一个数组变量,然后用完就不管了,根本也没有管数组的释放,因为超过数组的作用范围后,自动就销毁了,释放了占用的栈内存。
    这个栈是每个进程中的,进程中创建一个线程,就会为线程在进程空间中分配一块内存给线程使用,这个内存就是线程栈。我们说的栈都是指线程栈。线程栈有一定大小,用于函数调用存储参数,记录调用函数的返回地址,用于函数返回到调用函数的位置继续执行,还用于分配局部自动变量。
    栈的空间就是一段内存。我们使用数组就在栈中分配,那么对数组操作,起始就是对数组对应的栈地址进行操作。下面给出数组使用的代码,在VC中编译通过。

char t[5];
t[8]=0;

    这两句代码的意思是:分配一个5个字节的数组,然后对数组中的第九个元素赋值为0.
    这就已经越界了。但是在VC编译时不报错。在Debug模式下,运行会报错。而在Release模式下,没有任何问题。为什么呢?
    虽然数组越界了,但是Release模式依然放行,这就给程序带来隐患,这个很可能在运行过程中破坏了线程栈,导致程序崩溃。比如,t[8]的位置刚好是调用函数返回地址,地址是四个字节,但是结果其中一个字节却被清零了,这样导致返回地址破坏了。那么函数无法返回,从而程序崩溃了。
    那为什么只有在Debug模式下才能检测出来而Release模式为什么不行呢?为什么编译又能通过呢?
    在Debug模式下,代码中是加入了检测跟踪代码的,可以便于调试,自然这样就可以容易检查出来。因为加入了调试代码,自然生成的EXE就比较大。而Release模式则去掉了这些跟踪检测代码,自然就不会检测这些东西了。对于这些的检测,程序员要在Debug模式下通过,最后发布是才用Release模式生成EXE,不要使用Release模式调试程序哦。
    而编译通过的原因,则是因为,及时数组越界,但是因为越界的范围比较小,操作的内存地址在线程栈的范围之中,不可能出现内存操作违规的情况,编译器也没办法分清楚正确与否,所以也就通过了。
    但是,编译通过和运行暂时没有出现问题,不代表没有问题,越界后是有隐患的。一般情况还是很严重的。特别要防范好,以免造成巨大的损失。
    这个问题在将局部变量的地址当做函数返回值,这个问题也是很难检查出来的,如果不对这个地址赋值等,一般也不会有什么问题,一旦对这个赋值,且这个地方正好被其他参数使用了,那么就会造成线程栈破坏,导致程序崩溃。而当时你很可能发现不了这个问题,却留下了隐患。
    所以,如果程序崩溃出现线程栈破坏的情况,请检查数组越界、返回局部变量地址等等问题。在开发软件时一定要养成好习惯,不要犯这种错误,一般这种错误时很难定位的。