2010-07-21 46 views
4

我在一些腳本語言中相當能幹,但我終於強迫自己學習C語言。我只是在玩一些基本的東西(現在是I/O)。我怎樣才能分配堆內存,在分配的內存中存儲一​​個字符串,然後吐出來呢?這就是我現在所擁有的,我怎樣才能使它正常工作?Malloc和scanf

​​

目前我收到奇怪的輸出像'8'\'。

+0

你並不需要從'的malloc和''包括投的返回類型'在ISO C(3)。 – sarnold 2010-07-21 02:34:34

+0

值得指出的是,您應該在這裏使用堆棧。 – dicroce 2010-07-21 02:36:27

回答

7
char *toParseStr = (char*)malloc(10); 
    printf("Enter string here: "); 
    scanf("%s",toParseStr); 
    printf("%s",toParseStr); 
    free(toParseStr); 

首先,scanf中的字符串指定了它將要接收的輸入。爲了在接受鍵盤輸入之前顯示一個字符串,使用printf,如圖所示。

其次,您不需要取消toParseStr,因爲它指向的大小爲10的字符數組與您分配的malloc如果您正在使用的功能,它會指向另一個內存位置,然後&toParseStr是必需的。

例如,假設您想編寫一個函數來分配內存。然後,您需要&toParseStr,因爲您正在更改指針變量的內容(這是內存中的一個地址---您可以通過打印其內容來查看自己的內容)。

void AllocateString(char ** ptr_string, const int n) 
{ 
    *ptr_string = (char*)malloc(n) 
} 

正如你可以看到,它接受char ** ptr_string其內容,其存儲一個指示字的存儲器位置的指針,其存儲所分配的塊的第一個字節的存儲器地址(malloc操作之後)的n字節(現在它有一些垃圾內存地址,因爲它是未初始化的)。

int main(int argc, char *argv[]) 
{ 
    char *toParseStr; 
    const int n = 10; 
    printf("Garbage: %p\n",toParseStr); 
    AllocateString(&toParseStr,n); 
    printf("Address of the first element of a contiguous array of %d bytes: %p\n",n,toParseStr); 

    printf("Enter string here: "); 
    scanf("%s",toParseStr); 
    printf("%s",toParseStr); 
    free(toParseStr); 

    return 0; 
} 

第三,建議釋放你分配的內存。即使這是你的整個程序,並且這個內存將在程序退出時被釋放,但這仍然是一個好習慣。

+2

即使在一個小程序中也可以釋放+1。讓我想起「小滴滴變成海洋」。 ;-) – 2010-07-21 03:54:01

+0

您應該在打印提示符並調用scanf之間調用'fflush(stdout);'。大多數實現會做到這一點,讓你彬彬有禮,但它不是強制性的。 – 2010-07-21 11:07:47

3

,因爲它已經是一個指針

也呼籲free(toParseStr)你不需要toParseStr之前&scanf事後

+1

根據bball的系統,可能需要在該printf中放置一個「\ n」,以便正確顯示事物。另外,10個字符是一個非常短的字符串。 – George 2010-07-21 02:35:56

+1

雖然這是事實,但這並不是問題的根源(在這種情況下'&'是不必要的,但是無害的)。 – 2010-07-21 02:36:14

+2

@Jerry它是無害的,因爲格式說明符沒有指定任何參數,但是一旦他修復它在你的答案中有一個%s就會導致段錯誤 – 2010-07-21 02:37:42

9

你需要給scanf轉換格式,因此它知道你想讀一個字符串 - 現在,你只是顯示在你分配的內存中發生的任何垃圾。而不是試圖描述所有的問題,這裏的一些代碼,至少應該接近工作:

char *toParseStr = malloc(10); 
printf("Enter a string: "); 
scanf("%9s", toParseStr); 
printf("\n%s\n", toParsestr); 
/* Edit, added: */ 
free(toParseStr); 
return 0; 

編輯:在這種情況下,free荷蘭國際集團字符串不作任何真正的區別,但像其他人那樣指出,它是仍然是培養的好習慣。

+1

+1正確使用'scanf'中的'%9s'。 – sarnold 2010-07-21 02:42:47

+1

與某些人的看法相反,在執行讀取操作之前,刷新'stdout'對於確保提示出現是不必要的,除非實現被正確地破壞了。對於那些真正關心的人,請參閱§7.19.3。如果可以確定*不*指向交互設備,'stdout'只能被完全緩衝。 – 2010-07-21 03:02:06

+0

你錯了。 'stdout'仍然可以**行緩衝**,這意味着直到打印換行符纔會顯示任何內容。 POSIX建議實現在讀取時刷新stdout和其他這樣的行緩衝流,但是掃描行緩衝流的打開文件列表(特​​別是使用線程和鎖定)並且實現可能選擇不執行所以出於很好的理由。據我所知,ISO C對緩衝語義幾乎沒有要求。所以你**應該**沖洗! – 2010-07-21 11:10:43

0

首先,使程序不能正常工作的錯誤:scanf(3)採用格式字符串,就像printf(3)一樣,不是爲用戶打印的字符串。其次,您傳遞的是指針toParseStr的地址,而不是指針toParseStr

我也從您的電話malloc(3)刪除不必要的演員。

你的程序仍然需要改進的地方是使用scanf(3)a選項爲你分配內存 - 這樣一些小丑將十個字符放入你的字符串中並不會開始st on無關的內存。 (是的,C就會讓別人幾乎覆蓋整個地址空間有了這個程序,書面巨安全漏洞。:)

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char *argv[]) 
{ 
    char *toParseStr = malloc(10); 
    printf("Enter a short string: "); 
    scanf("%s",toParseStr); 
    printf("%s\n",toParseStr); 
    return 0; 
} 
+1

'scanf'沒有'a'選項。這是一個GNU擴展,它不僅是非標準的,而且還有** CONFLICTS **和ISO C('%a'是讀取浮點數的說明符之一!)。應該絕對避免。 – 2010-07-21 11:15:29

+0

謝謝;我不知道這個擴展與ISO C. – sarnold 2010-07-22 09:19:51

4

數據使用scanf()(或fscanf()你不控制)與標準「 %s「說明符是讓你自己陷入緩衝區溢出困境的一種近乎確定的方式。

經典的例子是,我在你的程序中輸入字符串「這個字符串超過10個字符」,混沌將隨之而來,貓和狗將開始一起睡覺,裸露的奇點可能會出現並消耗地球(大多數人只是陳述「未定義的行爲」,但我認爲我的描述更好)。

我積極勸阻使用無法提供保護的功能。我會敦促你(特別是作爲C的新手)使用fgets()來讀取你的輸入,因爲你可以更容易地控制緩衝區溢出,它比scanf()更適合於簡單的線路輸入。

一旦你有了一條線,你就可以在其上調用sscanf()到你心中的內容,順便說一句,你不需要在這個特定的情況下做,因爲你只是得到一個原始字符串。

我會用:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define BUFFSZ 10 

int main(int argc, char *argv[]) { 
    char *toParseStr = malloc(BUFFSZ+2); 
    if (toParseStr == NULL) { 
     printf ("Could not allocate memory!\n"); 
     return 1; 
    } 
    printf ("Enter a string: "); 
    if (fgets (toParseStr, BUFFSZ+2, stdin) == NULL) { 
     printf ("\nGot end of file!\n"); 
     return 1; 
    } 
    printf("Your string was: %s",toParseStr); 
    if (toParseStr[strlen (toParseStr) - 1] != '\n') { 
     printf ("\nIn addition, your string was too long!\n"); 
    } 
    free (toParseStr); 
    return 0; 
} 
+1

+1有衝突,但我補充說,雖然'fgets'確實有優勢,scanf和fscanf都有防止緩衝區溢出的措施。 – 2010-07-21 02:46:06

+0

@Jerry雖然我很少看到人們用「%s」使用寬度說明符,但這是一個很好的觀點:-)因爲我的大多數控制檯I/O代碼都傾向於具有基於行的輸入,所以%s不適合獲得空白空間。但是,由於您的答案在這種情況下確實是正確的,所以您+1。 – paxdiablo 2010-07-21 03:02:12

+2

另一個有趣的可能性是'scanf(「%9 [^ \ n]」,your_string);'''scanf'的面向行的字符串輸入,無論值多少錢。 – 2010-07-21 03:10:23