動態數組空間釋放——指針誤操作


簡介:

本文簡要說明在釋放動態數組需要注意的地方,主要通過以下兩個實例的練習,來加深理解數組空間釋放:
  示例一:根據輸入參數動態創建數組。
  示例二:從標准輸入設備讀入的元素數據建立一個int 型 vector 對象,然后動態創建一個與該 vector 對象大小一致的數組,把 vector 對象的所有元素復制給新數組。

關於創建動態數組的詳細說明,可參考《C++Primer Fourth Edition-Section 4.3.1》。
本文也是基於書中習題(Exercise 4.28)的一些學習與思考,比較重要的地方引用書中的原話,在此做出聲明。

 

實現:

示例一:根據輸入的數組容量參數,實現int型動態數組創建,輸出數組的內容,然后就釋放了數組空間。
C++為指針提供“delete []表達式”釋放指針所指向的數組空間。在關鍵字 delete 和指針之間的空方括號對是必不可少的: 它告訴編譯器該指針指向的是自由存儲區中的數組,而並非單個對象。 如果遺漏了空方括號對,這是一個編譯器無法發現的錯誤,將導致程序在運行時出錯。

#include <cstring>
#include <iostream>
#include <vector>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[])
{
        if(2!=argc)
        {
                cout<<"參數錯誤, 請使用:"<<argv[0]<<" 數字"<<endl;
                return 1;
        }

        int arr_size=atoi(argv[1]);

        cout<<"定義動態數組ip,空間大小為:"<<arr_size<<",且初始化為:0"<<endl;
        int *ip=new int[arr_size]();    //圓括號的作用是實現數組的初始化,int型數據默認值0。
   如果為類類型,則按默認構造函數初始化
for(int i=0; i<arr_size; ++i) { cout<<"ip["<<i<<"]="<<*(ip+i)<<endl; } cout<<"釋放數組空間"<<endl; delete [] ip;
     ip=NULL;
return 0; }

 

實例二:實現將vector對象保存的數據復制到動態創建的數組中。下面的代碼在編譯時不會報錯,在執行的時候會提示錯誤

#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
    vector<int> ivect;
    int input=0;
    while(cin>>input)
    {
        ivect.push_back(input);
        if(0==input)break;    // 方便gdb調試
    }

    int *ip=new int[ivect.size()];
    for(vector<int>::iterator iter=ivect.begin(); iter!=ivect.end(); ++iter,++ip)
    {
        *ip=*iter;
        cout<<*ip;
    }
    cout<<endl;

    delete [] ip;
ip=NULL;
return 0; }

 

*** glibc detected *** ./ar: free(): invalid pointer: 0x094f6080 ***
======= Backtrace: =========
/lib/libc.so.6[0x77c261]
/usr/lib/libstdc++.so.6(_ZdlPv+0x22)[0x35f89f2]
/usr/lib/libstdc++.so.6(_ZdaPv+0x1e)[0x35f8a4e]
./ar[0x8048bad]
/lib/libc.so.6(__libc_start_main+0xe6)[0x724bb6]
./ar[0x80489f1]
======= Memory map: ========
005b6000-005d3000 r-xp 00000000 fd:00 51325      /lib/libgcc_s-4.4.2-20091027.so.1
005d3000-005d4000 rw-p 0001c000 fd:00 51325      /lib/libgcc_s-4.4.2-20091027.so.1
006ec000-0070a000 r-xp 00000000 fd:00 56668      /lib/ld-2.11.so
0070a000-0070b000 r--p 0001d000 fd:00 56668      /lib/ld-2.11.so
0070b000-0070c000 rw-p 0001e000 fd:00 56668      /lib/ld-2.11.so
0070e000-0087c000 r-xp 00000000 fd:00 56669      /lib/libc-2.11.so
0087c000-0087d000 ---p 0016e000 fd:00 56669      /lib/libc-2.11.so
0087d000-0087f000 r--p 0016e000 fd:00 56669      /lib/libc-2.11.so
0087f000-00880000 rw-p 00170000 fd:00 56669      /lib/libc-2.11.so
00880000-00883000 rw-p 00000000 00:00 0 
008a8000-008d0000 r-xp 00000000 fd:00 56684      /lib/libm-2.11.so
008d0000-008d1000 r--p 00027000 fd:00 56684      /lib/libm-2.11.so
008d1000-008d2000 rw-p 00028000 fd:00 56684      /lib/libm-2.11.so
00e0a000-00e0b000 r-xp 00000000 00:00 0          [vdso]
03541000-0362f000 r-xp 00000000 fd:00 56729      /usr/lib/libstdc++.so.6.0.13
0362f000-03633000 r--p 000ed000 fd:00 56729      /usr/lib/libstdc++.so.6.0.13
03633000-03635000 rw-p 000f1000 fd:00 56729      /usr/lib/libstdc++.so.6.0.13
03635000-0363b000 rw-p 00000000 00:00 0 
08048000-0804b000 r-xp 00000000 fd:00 152332     /home/kang/Desktop/CPPprimer/ar
0804b000-0804c000 rw-p 00002000 fd:00 152332     /home/kang/Desktop/CPPprimer/ar
094f6000-09517000 rw-p 00000000 00:00 0          [heap]
b78c3000-b78c5000 rw-p 00000000 00:00 0 
b78da000-b78dd000 rw-p 00000000 00:00 0 
bfc2f000-bfc44000 rw-p 00000000 00:00 0          [stack]
Aborted (core dumped)

 

  看到了free()、Memory、dumped,沒錯,這就是傳說中的內存泄露,指針操作不當,就會看到上面的一堆代碼。在gdb調試時,發現使用ctrl+d命令結束數據輸入時直接退出調試,為了調試方便,增加了判斷輸入是否為0的判斷。

  單步到倒數第三行代碼:
            cout<<endl;

  一切仍然OK,再執行:   //其實錯誤在此前已經悄悄的出現,只在等待delete時爆發啦
    delete [] ip;
  上面的一堆錯誤就出來了,難道是中括號前后的空格導致的?它怎么地就內存溢出了呢?

 

  再看看數組的創建:
    int *ip=new int[ivect.size()];
  此new表達式分配了一個含有 ivect.size() 個 int 型元素的數組
,並返回指向該數組第一個元素的指針,此返回值初始化了指針 ip

  對,ip指向數組的第一個元素。那delete時的ip指針也是指向第一個元素么?
    delete [] ip;
  根據前面提到的,delete語句應該是釋放ip指針所指向的數組空間,所以此時的ip還是指向數組的第一個元素。那錯誤出在那里了呢?
  難道是ip指針被指向的不是第一個元素,再看看for循環
    for(vector<int>::iterator iter=ivect.begin(); iter!=ivect.end(); ++iter,++ip)
  ++ip,整個循環結束后,ip指向了數組的最后一個元素,后面沒有將ip指針移回原處。噢,原來是在這里修改了ip指針。

  錯誤終於找到了。好了,修改下試試。將
    delete [] ip;
  修改為:
    delete [] (ip - ivect.size());
  編譯,運行,OK啦。

 

其實提倡的做法是,再定義一個int指針ip_temp,指向ip所指的空間,在整個操作過程中使用ip_temp,只是在最后釋放空間的時候使用ip就可以啦。

感謝zdd的提醒,在釋放空間后,將ip=NULL,在復雜的程序中,在使用該指針前通過判斷指針是否為NULL來確定指針的合法性,防止使用不存在的內存空間。

 

總結:

1.指針和數組太危險,推薦使用vector,verctor的優勢還是很多的
2.出現上面的錯誤最大的原因還是在自己沒有理解透指針和數組的本質意義,還是需要多做練習。像這些簡單的練習,動手啦,才能知道哪里會錯,錯啦才知道改,改啦就會記憶深刻。
3.堅持讀完這本書,練好基本功


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM