2012-08-22 14 views
1

前段時間我在這個網站上看到了一個功能,那就是我爲了我的使用而做了一些調整。這種字符串檢索方法的任何缺點?

這是一個函數,它使用getc和stdin來檢索一個字符串並精確地分配儘可能多的內存,因爲它需要包含該字符串。然後它只是返回一個指向分配內存的指針,該內存被所述字符串填充。

我的問題是有任何缺點(除了必須手動釋放分配的內存)到這個功能?你會做什麼來改善它?

char *getstr(void) 
{ 
    char *str = NULL, *tmp = NULL; 
    int ch = -1, sz = 0, pt = 0; 

    while(ch) 
    { 
     ch = getc(stdin); 
     if (ch == EOF || ch == 0x0A || ch == 0x0D) ch = 0; 
     if (sz <= pt) 
     { 
      sz++; 
      tmp = realloc(str, sz * sizeof(char)); 
      if(!tmp) return NULL; 
      str = tmp; 
     } 
     str[pt++] = ch; 
    } 

    return str; 
} 

這裏使用您的建議後,我更新的代碼,我決定只使用256個字節的緩衝區,因爲使用該功能的用戶輸入。

char *getstr(void) 
{ 
    char *str, *tmp = NULL; 
    int ch = -1, bff = 256, pt = 0; 

    str = malloc(bff); 
     if(!str) 
     { 
      printf(\nError! Memory allocation failed!"); 
      return 0x00; 
     } 
    while(ch) 
    { 
     ch = getc(stdin); 
     if (ch == EOF || ch == '\n' || ch == '\r') ch = 0; 
     if (bff <= pt) 
     { 
      bff += 256; 
      tmp = realloc(str, bff); 
      if(!tmp) 
      { 
       free(str); 
       printf("\nError! Memory allocation failed!"); 
       return 0x00; 
      } 
      str = tmp; 
     } 
     str[pt++] = ch; 
    } 
    tmp = realloc(str, pt); 
    if(!tmp) 
    { 
     free(str); 
     printf("\nError! Memory allocation failed!"); 
     return 0x00; 
    } 
    str = tmp; 

    return str; 
} 
+0

提醒我這一個:http://stackoverflow.com/a/8164021/714501 – cnicutar

+0

是的!這正是我第一次看到這個功能的地方。 –

+0

您的修訂版本更合理。你可能也想考慮爲每個realloc()乘以2,並在他的回答的評論中看到Qnan和我之間的討論。雖然如你所說,這只是手動用戶輸入,你有什麼是好的。 – delicateLatticeworkFever

回答

2

這是過分節儉的海事組織,並且犯了犧牲性能的錯誤,以便節省大量的記憶,這在大多數情況下都是毫無意義的,我想。像realloc這樣的分配調用對系統來說可能很麻煩,而且這裏每個字節都是這樣做的。

只需要一個本地緩衝區(比如4KB)讀入,然後根據實際讀入的內容的長度分配返回字符串會更好。請記住,正常系統上的堆棧*無論如何都是4-8MB,無論您是否使用它。如果讀取的字符串長度超過4KB,則可以編寫一個類似的循環來分配並複製到返回字符串中。因此,類似的想法,但堆分配會發生每4096個字節,而不是每個字節,所以,例如,你有4096的初始緩衝區,當你用完malloc 4096爲返回字符串和複製,繼續讀入緩衝區(從開始),如果讀取另外1000個字節,則將其重新分配給5097並返回。

我認爲這是一個初學者通過逐字節接近最小化堆分配而癡迷的常見錯誤。即使KB by KB也有點小;系統分配頁面(4 KB),您可能會將其與自己對齊。

*內存提供本地存儲功能。

+0

在大多數系統上,堆棧有幾兆字節的*地址空間分配,但實際分配的內存將是當前大小,最多爲整數頁(例如,4KB塊)。 –

+0

@JerryCoffin:絕對地,OP應該讀到虛擬地址空間與實際內存之間的區別。不過,我認爲並非所有的操作系​​統都以相同的方式處理這個問題,但是,其中一些操作系統將虛擬地址空間視爲一個具體的承諾,因此如果操作系統提供了4MB的地址空間,那麼它還會留出4MB的實際內存來支持該承諾(儘管沒有一個虛擬<->實際映射分配)。其他人不這樣做(這就是爲什麼默認情況下,malloc或realloc永遠不會在linux上返回null)。等等,只是試圖提出一些基本原則;) – delicateLatticeworkFever

2

是,主要的問題是,realloc是相當緩慢和反覆調用它的每個字符通常是一個壞主意。

嘗試分配固定的內存量,開始時,說N=100人物,當你需要更多,得到的東西像2*N,然後4*N等。您只會花費超過內存的兩倍,但在運行時節省很多

+0

更好的想法:分配一個大緩衝區,然後如果你需要溢出它,使用realloc來增加你的空間。如果你真的想削減額外的存儲空間,你可以malloc,copy和free,或者甚至在最後重新分配。 – FrankieTheKneeMan

+0

當你說主要問題是你暗示有其他人嗎?你將做點什麼不同的? –

+0

我首先想到了分配一個大的內存塊,但由於某種原因,我發現分配更多內存的想法比需要的有點令人不安,但確實最後調用一次realloc會比調用多個實例更高效realloc。 –

2
  1. 由於沒有很好的理由,它取決於'\ n'=='0xa'和'\ r'=='\ 0d'。如果您的意思是\r\n,請使用它們。
  2. 它可能會不合理地緩慢,重新分配你讀的每個字符。
  3. sizeof(char)保證是1,所以沒有意義。
  4. 如果你已經分配了一塊內存,那麼realloc失敗,你返回NULL而不返回或釋放str,從而泄漏內存。
  5. 該接口無法指示部分故障,如#4所示。你所能做的只是返回一個字符串。給定一個巨大的輸入字符串,你沒有辦法表明你已經閱讀部分,但不是全部。
+0

哎呀很好的捕獲我沒有看到我忘了釋放'str'的​​值 –

2

這裏是前一些意見,其他的答案中包含更多一些:

  1. 它是由1個字節同時增大緩衝區,這樣做許多不必要的來電realloc()
  2. 如果realloc()失敗,則以前的緩衝區丟失。
  3. 這不是getline(),雖然它當然更便攜。
  4. 對於換行和回車來說,硬編碼的ASCII值也不是很方便,用'\n''\r'代替。
相關問題