2015-01-17 56 views
0

我一直在試圖編寫一個簡短的程序,允許用戶添加條目到「數據庫」,列出他們已經放入的條目,並且能夠清除所有條目而無需結束該程序。下面是我得到了創建和清除結構數組

#include <cstdlib> 
#include <iostream> 
#include <string> 
using namespace std; 

struct BIRTH 
    {int month; int year;}; 
struct ID 
    {string name; bool vip; float score; 
     struct BIRTH date;} ; 

int main(int argc, char** argv) { 

    ID **ptrarr; 
    ptrarr = new ID * [10]; 
    for (int r=0; r<10; r++) 
     {ptrarr[r] = new ID[1] ;} 


    int counter = 0; 

    while(counter<100){ 
    cout << "Type add to create a new entry" << endl; 
    cout << "Type list to see all entries" << endl; 
    cout << "Type clear to delete all entries" << endl; 
    cout << "Type exit to terminate" << endl; 

    string command = "0"; 

    getline (cin,command); 

    if(command=="add") 
     { 
     cout << "Enter name" << endl; 
     getline (cin,ptrarr[counter][1].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> ptrarr[counter][1].vip; 
     cout << "Enter score" << endl; 
     cin >> ptrarr[counter][1].score; 
     cout << "Month of birth" << endl; 
     cin >> ptrarr[counter][1].date.month; 
     cout << "Year of birth" << endl; 
     cin >> ptrarr[counter][1].date.year; 
     counter++; 

     } 
    else if(command=="list") 
     { 
      for (int i=0; i<counter; i++) 
      {int n=i+1; 
      cout << n << " " 
       << ptrarr[i][1].name << " "; 
        if (ptrarr[i][1].vip) 
        {cout << "VIP ";} 
      cout << "Score: " << ptrarr[i][1].score << "  " 
       << "Born: " << ptrarr[i][1].date.month << "/" << ptrarr[i][1].date.year << endl; 
        } 
    } 
    else if(command=="clear") 
    {delete[] ptrarr; 

     cout << "Entries cleared" << endl;} 
    else if(command=="exit") 
     {return 0;} 
    else 
     cout << "try again" << endl; 
    } 
return 0; 
} 

現在,這裏的交易:將以下代碼編譯成功,但是當我在「添加」命令類型,程序崩潰(成就解鎖,沒想到有可能獲得用這麼短的代碼)。最重要的是該數組由多種類型的結構組成,並且「清除」命令清除了數組中的所有條目。

注意:我知道有一千個更好的方法來編寫這段代碼,但是我正在寫它來練習我迄今爲止關於C++所介紹的內容。所以,除非它是絕對必要的代碼運行,請不要引入任何新的噱頭=)

回答

0

您正在創建一個指針數組,其中每一個都指向一個元素:

ptrarr[r] = new ID[1] ; 

,您可以用ptrarr[r]使用的最大指數爲0。由於您使用的是ptrarr[counter][1],因此您正在訪問超出範圍的內存。這導致未定義的行爲。崩潰就是這樣一個未定義的行爲。

您的代碼還有其他問題需要解決。

更多越界內存訪問

的您正在使用:

int counter = 0; 
while(counter<100){ 

... 

getline (cin,ptrarr[counter][1].name); 

即再次將導致不確定的行爲,如果counter > 10因爲你ptrarr只分配10指針。

刪除內容

您正在使用:

else if(command=="clear") 
    { 
    delete[] ptrarr; 

    cout << "Entries cleared" << endl; 
    } 

有幾個問題是:

  1. 你有內存泄漏。你永遠不會撥打delete []什麼ptrarr[0] - ptrarr[9]指向。您必須使用:

    else if(command=="clear") 
    { 
        for (int i = 0; i < 10; ++i) 
        { 
         delete [] ptrarr[i]; 
        } 
    
        delete[] ptrarr; 
    
        cout << "Entries cleared" << endl; 
    } 
    

    請記住,每個分配都必須具有相應的釋放。否則,你正在泄漏記憶。

  2. 一旦你撥打delete [] ptrarr;,它指向懸空記憶。我沒有看到任何代碼在您繼續使用它時爲ptrarr重新分配內存。

您需要重新分配內存和復位counter0當用戶選擇「清除」。

我的建議

你不兩級指針。你只需要像:

int const MAX_ITEMS = 100; 
ID* IDarr = new ID[MAX_ITEMS]; 

相反的ptrarr[counter][1],使用IDarr[counter]

while聲明中使用MAX_ITEMS而不是幻數100

int counter = 0; 
while(counter<MAX_ITEMS){ 

當處理「清除」時,您不需要取消分配或分配內存。只需重置counter

else if(command=="clear") 
    { 
    counter = 0; 
    cout << "Entries cleared" << endl; 
    } 

請確保在從main返回之前釋放內存。

以下是完整main函數的變化:

int main(int argc, char** argv) { 

    const int MAX_ITEMS = 100; 
    ID* IDarr = new ID[MAX_ITEMS]; 

    int counter = 0; 
    while(counter < MAX_ITEMS){ 
     cout << "Type add to create a new entry" << endl; 
     cout << "Type list to see all entries" << endl; 
     cout << "Type clear to delete all entries" << endl; 
     cout << "Type exit to terminate" << endl; 

     string command = "0"; 

     getline (cin,command); 

     if(command=="add") 
     { 
     cout << "Enter name" << endl; 
     getline (cin, IDarr[counter].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> IDarr[counter].vip; 
     cout << "Enter score" << endl; 
     cin >> IDarr[counter].score; 
     cout << "Month of birth" << endl; 
     cin >> IDarr[counter].date.month; 
     cout << "Year of birth" << endl; 
     cin >> IDarr[counter].date.year; 
     counter++; 

     } 
     else if(command=="list") 
     { 
     for (int i=0; i<counter; i++) 
     { 
      int n=i+1; 
      cout << n << " " << IDarr[i].name << " "; 
      if (IDarr[i].vip) 
      { 
       cout << "VIP "; 
      } 
      cout 
       << "Score: " << IDarr[i].score << "  " 
       << "Born: " << IDarr[i].date.month << "/" << IDarr[i].date.year << endl; 
     } 
     } 
     else if(command=="clear") 
     { 
     counter = 0; 

     cout << "Entries cleared" << endl; 
     } 
     else if(command=="exit") 
     { 
     // Don't use return 0; 
     // Just break out of the while loop so that memory 
     // can be deallocated at the end of this function. 
     break; 
     } 
     else 
     cout << "try again" << endl; 
    } 

    delete [] IDarr; 

    return 0; 
} 
+0

真的很有幫助=)現在我知道究竟是什麼導致代碼崩潰。此外,從數組中刪除每個條目的好處。我的直覺告訴我,這是我應該做的,但我不知道如何。我現在所做的是,該選項清除條目,而不是數組作爲整體,重置計數器,然後再次創建數組,使用與主開頭相同的代碼 – Quit

0

數組索引從0開始。

ptrarr[counter][1]ptrarr[counter]第二個元素。 ptrarr[counter]指向一個元素的數組。

2

全部替換ptrarr[counter][1]ptrarr[counter][0]解決了這個問題。

進一步建議:

此代碼有冗餘:

ID **ptrarr; 
ptrarr = new ID * [10]; 
for (int r=0; r<10; r++) 
{ptrarr[r] = new ID[1] ;} 

將其替換爲:

ID *ptrarr; 
ptrarr = new ID [10]; 

那麼你不需要額外的[0]每個ptrarr的末尾[counter]

二,功能使你的代碼更易讀:

if(command=="add") 
    add(); 
else if(command=="list") 
    list(); 
else if(command=="clear") 
    clear(); 
else if(command=="exit") 
    return 0; 
else 
    cout << "try again" << endl; 

然後決定是在一個較小的區域進行

III(對大型項目的良好做法。)。代碼中存在另一個錯誤:

else if(command=="clear") 
{delete[] ptrarr; 

    cout << "Entries cleared" << endl;} 

在這裏您應該重置計數器。此外,如果你認爲我的觀點()這部分是好的。否則,如果將newfor循環一起使用,恐怕您需要使用delete以及for循環。僅僅刪除數組樹的根就會帶來內存泄漏!

此外,如果您通過刪除清除了列表,那麼您是否需要將數據存儲在列表中?在鏈表中使用刪除是一個好主意,但它不適用於此。只需重新設置計數器即可完成工作,並且不再顯示列表中的ID。列表中的for只計數到計數器。

如果您退出該程序,是不是釋放內存?

我說

delete [] ptrarr; 

有利於處於退出。

+0

嗨,非常感謝這裏的幫助。說到計數器重置,我意識到了這一點,不知道爲什麼它沒有在這裏引用代碼。但我想清楚重置內存主要用於培訓目的,這個代碼的第一個版本只是重置櫃檯,我覺得作弊=) – Quit

+0

這不是作弊。因爲數組總是佔用10個對象的內存,無論是否使用它們。該內存分配允許您稍後添加對象。如果你釋放內存,你將無法再添加任何對象,除非你再次分配10個塊!所以不用擔心只能重新設置。這是正確的工作。 – Arashium

0

試試這個:

if(command=="add") { 
     cout << "Enter name" << endl; 
     getline (cin,ptrarr[counter][0].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> ptrarr[counter][0].vip; 
     cout << "Enter score" << endl; 
     cin >> ptrarr[counter][0].score; 
     cout << "Month of birth" << endl; 
     cin >> ptrarr[counter][0].date.month; 
     cout << "Year of birth" << endl; 
     cin >> ptrarr[counter][0].date.year; 
     counter++; 
    } 
    else if(command=="list") { 
     for (int i=0; i<counter; i++){ 
      int n=i+1; 
      cout << n << " "<< ptrarr[i][0].name << " "; 
      if (ptrarr[i][0].vip){ 
       cout << "VIP "; 
      } 
      cout << "Score: " << ptrarr[i][0].score << "  " 
      << "Born: " << ptrarr[i][0].date.month << "/" << ptrarr[i][0].date.year << endl; 
     } 
    } 

結論

  • 正如你初始化counter 0,你應該有使用0索引來計算第一個因素;
  • 上市同樣如此。
  • 數組基於0索引。