2011-12-31 37 views
3

對於類,我使用C++創建了一個shell。從C++開始工作已經有一段時間了,所以我遇到了一些麻煩。該項目的一個要求是我必須使用read()系統調用。無法將內存分配給C++中的唯一地址

我需要保持一個命令歷史記錄(類似於bash的,如果你按向上箭頭),其存儲最近20個命令。我覺得做到這一點的最好方法是使用指向先前語句的指針數組。我遇到了一個問題,無論我做什麼,包含用戶輸入的字符串總是存儲在內存中的相同位置。爲了澄清,這意味着,如果用戶輸入5條語句,然後查看他/她的歷史記錄,他們將看到最近的語句5次。我的代碼看起來有點像這樣(我不得不削減一些東西出來,因爲在中間有很多的錯誤處理):

char *history[20]; 
int historyCounter = 0; 

while(true){ 
    char currLine[65]; 
    int charsRead = read(0,currLine,65); 

    char tmp[charsRead]; 
    strcpy(tmp,currLine); //This is my attempt to ensure the char[] is stored int a 
         //unique location every time, but this attempt failed. 

    history[historycounter] = tmp; 
    historycounter++; 
} 

只是要注意,在我的源代碼,我做處理時的情況historycounter> 19.只是不在這個片段。

如果需要任何更多的澄清,我很樂意提供。這是我第一次在堆棧溢出發帖,所以如果我犯了新手的任何錯誤,我都會提前道歉。如果解決方案很痛苦,我也很抱歉。我一直在看這一段時間,而我完全有可能沒有直截了當。

+0

不要使用'這裏strcpy' - 'read'不空終止其輸出等'charsRead'不包括終止空字符的空間,也沒有任何終止空字符存在。您應該使用memcpy(並明確記錄長度)或添加終止字符(並確保在複製時爲其分配空間)。 – bdonlan 2011-12-31 21:07:20

回答

2

在這種情況下,tmp是最有可能是在相同的位置,上堆疊。它在每次迭代時分配,並在迭代結束時釋放。

history[historycounter] = tmp; 

會導致當你使用它,因爲你將使用一個局部變量的地址其範圍之外的未定義行爲。

如果你想確保唯一地址(和解決問題UB) - 使用new分配內存,並使用delete直到大功告成。確保完成所有分配的指針和delete跟蹤。

+0

哇...這解決了我的問題。我現在有點像一個白癡,但我絕對不怕回到另一個問題。非常感謝您的快速和有益的迴應!對此,我真的非常感激! – Tom 2011-12-31 20:50:36

+0

@Tom任何時候,對於第一個計時器,您的問題都是非常明確的表述和主題,你會驚訝它是多麼令人耳目一新。不要忘記接受答案。 – littleadv 2011-12-31 20:51:28

+3

更好的是,不要使用'new'。使用'std :: string',或者'std :: vector '。更好的是,內存池不會導致碎片。 – 2011-12-31 20:57:56

1

你在用C++或C做你的功課嗎?

littleadv告訴你爲什麼你的程序表現不正確。您從不分配字符串數組,而只是有一個字符串指針數組,但它們最終都指向由tmp標識的相同堆棧位置。

然而,而不是做你自己的指針操作,不像C,C++併爲您提供一些較高級別的選項。例如,你可以聲明你的數組作爲

std::vector<std::string>  listOfCommands; 

這會照顧所有的內存管理的爲您服務。也可以考慮創建一個類來封裝最後N個命令的存儲和檢索,而不是直接使用全局變量。提供最少的訪問該列表的公共函數(例如,數組和std :: vectors允許您修改任何元素,但在您的應用程序中,您只需要寫入結尾,因此應該只有一個寫入函數寫到最後)。然後你的listOfCommand將成爲一個私人數據,你的程序的其餘部分甚至不需要知道這些命令是如何存儲在內存中的。

這在C什麼編程++(以及任何其他OO)的語言是什麼。除了全局變量之外,還可以創建隱藏大部分複雜性的自包含構建塊(即類),然後創建依賴更簡單塊的更大塊。繼續這樣做,直到你的整個應用程序完成。

+0

爲什麼這個人是唯一提倡妥善解決方案的人? WinRAR的。 – Puppy 2011-12-31 22:55:11

+0

@DeadMG因爲有時你應該做你所說的話,即使一些聰明的驢子認爲它的「壞」。這個答案不回答這個問題。它給出了另一種做事情的方式(你認爲是「適當的」,儘管我不知道使用動態分配的方式變得不合適),但這不是OP要求的。 – littleadv 2012-01-01 03:49:29

-1

只需使用一個2-d陣列,跳過關於設法確保唯一的地址和TMP緩衝所有這些東西。

const size_t MAX_LINE_LENGTH = 65; 
typedef char HISTORY_LINE[MAX_LINE_LENGTH+1]; //+1 for null terminator 
const size_t MAX_HISTORY_LENGTH = 20; 
HISTORY_LINE history[MAX_HISTORY_LENGTH]; 
int historyCounter = 0; 

while(true){ 
    char* currLine = history[historycounter]; 

    currLine[0] = '\0'; 
    int charsRead = read(0,currLine,MAX_LINE_LENGTH); 
    if(charsRead > 0) 
     currLine[charsRead] = '\0'; // ensure string is null terminated 

    historycounter++; 
} 

我跳過所有相同的錯誤檢查,但是添加了一行以確保字符串始終爲空。

+1

肯定會降低「MAGIC_BUFFER」代碼。 – Puppy 2011-12-31 20:56:51

+0

如果每個字符串的數量和大小有固定的限制,這比使用動態內存管理好得多,因爲它不會導致內存碎片(對於諸如shell之類的長時間運行的進程很重要)。 – 2011-12-31 20:59:07

+0

@DeadMG:爲什麼?這些常數是有名的,使用常量比通過原始代碼分散的幻數要好得多。如果您反對線路長度限制,請隨時選擇OP。 – 2011-12-31 21:00:31