iT邦幫忙

1

C語言輸入字串發生segmentation fault

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
main()
{
    int n,maxweitht;
    scanf("%d\n%d",&n,&maxweitht);//整數1、2
    int p[n],w[n],index = 0;
    char *str,*delim;
    gets(str);//字串1
    delim = strtok(str," ");
    while(delim != NULL)
    {
        puts(delim);
        delim = strtok(NULL," ");
    }
    //index = 0;
    gets(str);//字串2
    delim = strtok(str," ");
    while(delim != NULL)
    {
        puts(delim);
        delim = strtok(NULL," ");
    }
}

這是我的程式碼,我打算依序輸入2個整數後輸入2個字串,但是輸入完2個整數後就跳出segmentation fault,我將main()前3行註解掉後,就能正確輸入及輸出,請問問題出在哪呢?

2 個回答

3
海綿寶寶
iT邦大神 1 級 ‧ 2021-05-22 17:25:42
最佳解答

1.原本char *str只宣告一個「指標」,並沒有宣告保留「記憶體的空間」
然後gets(str)就有可能去蓋掉別的變數的記憶體空間
2.很久之前就不建議使用 gets 這個指令,可能得換本新一點的書

程式修改如下

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

#define STRLEN 100

main()
{   
    int n,maxweitht;
    scanf("%d\n%d\n",&n,&maxweitht);//整數1、2
    int p[n], w[n], index=0;
    char str[STRLEN], *delim;
    fgets(str, STRLEN, stdin);//字串1
    delim = strtok(str," ");
    while(delim != NULL)
    {
        puts(delim);
        delim = strtok(NULL," ");
    }
    //index = 0;
    fgets(str, STRLEN, stdin);//字串2
    delim = strtok(str," ");
    while(delim != NULL)
    {
        puts(delim);
        delim = strtok(NULL," ");
    }
}
看更多先前的回應...收起先前的回應...
vuj8104 iT邦新手 5 級 ‧ 2021-05-23 10:59:36 檢舉

請問第一點沒宣告保留記憶體的空間是指如果前面輸入2個整數而這字元型態的指標一直偵測不到輸入就會發生segmentation fault嗎?

不是,跟偵測不到輸入沒關係,跟記憶體存取比較有關係
詳情可以參考中文簡易版或者英文複雜版的解釋

vuj8104 iT邦新手 5 級 ‧ 2021-05-23 11:35:23 檢舉

所以綜合上面所說的,因為字元指標沒指定多少空間,加上gets()本身容易去拜訪其他變數的記憶體位址,所以才會發生segmentation fault,用宣告字元陣列搭配fgets()輸入字串就可以指定多大的記憶體空間解決這問題,請問我的理解這樣對不對?

簡單說是對
長一點說就是「造成segmentation fault的原因有很多情形,在你這隻程式是這樣解決沒有錯」
重點在於
寫 C 程式要自己小心記憶體的處理,尤其是用到指標的時侯

vuj8104 iT邦新手 5 級 ‧ 2021-05-23 13:23:21 檢舉

好的,謝謝

1
haward79
iT邦新手 3 級 ‧ 2021-05-23 13:13:45

以下是能正常執行的 code,僅供參考。

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

int main()
{
    int n = 0, maxWeight = 0;

    // 讀取兩個整數。
    scanf("%d\n%d", &n, &maxWeight);

    int index = 0;
    int p[n], w[n];
    char str[n];
    char *token = NULL;

    // 讀取字串。
    gets(str);

    // 以空白切割字串。
    token = strtok(str, " ");

    while(token != NULL)
    {
        puts(token);
        token = strtok(NULL, " ");
    }

    // 讀取字串。
    gets(str);

    token = strtok(str, " ");

    while(token != NULL)
    {
        puts(token);
        token = strtok(NULL, " ");
    }

    return 0;
}

我想會有問題的應該是以下三種形式:

char str1[10];
char *str2 = NULL;
char *str3 = "123";

str1 是一個字元陣列,佔據了記憶體 10 個 char 型態的記憶體空間。

str2 是一個指標,指標的用途是儲存記憶體空間的位址,由於初始化給了 NULL,因此 str2 目前指向的記憶體空間為空(暫無指向記憶體位址)。str2 實際佔據的記憶空間其實與「一個 char 型態的記憶體位址的空間」相同。

str3 也是一個指標,與 str2 不同的是:他的初始值是一個字串,所以 C 會根據字串的大小在記憶體空間中分配一個 3 個字元長度的陣列,並將 str3 這個指標指向的記憶體位址設為陣列的第一個元素!str3 實際佔據的記憶空間其實與「一個 char 型態的記憶體位址的空間」相同(跟 str2 一樣)。不同的部份是:str3 的狀況下,新增了長度為 3 的字元陣列,用來儲存 "123"。str2 的狀況下,沒有從記憶體中拿取一段字元陣列的空間!

這就是為什麼你原本用 char *str; 會出問題的原因,因為 str 只是用於存放記憶體位址,但卻沒有從記憶體中保留一段字元陣列的空間,用於稍後的 gets 儲存字串用!

若改成 char str[n]; 就不同了!因為 str 會在記憶體中取得長度為 n 的字元陣列,之後 gets 讀進來的字串才有地方存放。

另外提醒你要注意一下 coding style 還有變數的 naming convension。

vuj8104 iT邦新手 5 級 ‧ 2021-05-23 13:28:47 檢舉

謝謝分享,感恩

我要發表回答

立即登入回答