簡體   English   中英

從C中的文件讀取空格分隔的值時出現分段錯誤

[英]Segmentation fault when reading space separated values from file in C

我有一個包含空格分隔值的輸入文件

AA BB 4
A B
AB BA
AA CC
CC BB
A B 3 
A C
B C
c B

我想將第一行的內容分別移到s1s2n ,接下來的n行內容2個空格分隔的字符串,它們分別移到production_leftproduction_right 對於后續的行塊也將重復該布局。 樣本數據有兩個輸入塊。

我的代碼如下

int main()
{
    char *s1[20], *t1[20];
    char *production_left[20], *production_right[20];
    int n;
    FILE *fp;
    fp = fopen("suffix.txt","r");
    int iss=0;
    do{
         fscanf(fp,"%s %s %d",s1[iss],t1[iss],&n);
         int i=0;
         while(i<n)
         {
             fscanf(fp,"%s %s",production_left[i],production_right[i]);
             i++;
         }
    }while(!eof(fp));
}

每次出現細分錯誤。

很多C只是讓您牢記在心,您需要在代碼的什么范圍(塊)中使用哪些數據。 在您的情況下,您要使用的數據是數據文件各部分的第一行(或標題)。 為此,您擁有s1t1 ,但是您沒有什么可保留n因此可以將其與您的數據一起重用。 由於n包含每個標題下對production_leftproduction_right期望的索引數,因此只需創建一個索引數組,例如int idx[MAXC] = {0}; 存儲與每個s1t1關聯的每個n 這樣,您可以保留該值以供以后在迭代中使用。 MAXC只是為20定義的常數,以防止在代碼中使用幻數

接下來,您需要轉向對指針聲明及其使用的理解。 char *s1[MAXC], *t1[MAXC], *production_left[MAXC], *production_right[MAXC]; 聲明s1t1production_leftproduction_right 4 20指針數組 (每個指針 20個)。 指針是未初始化和未分配的 雖然每個都可以初始化為指針值 ,但是沒有任何與它們關聯的存儲 (已分配內存)允許復制數據。 您不能簡單地使用fscanf並為每個指針分配相同的指針值(如果最后一個值保留在范圍內,它們最終都將指向最后一個值)

因此,您有兩種選擇:(1)使用2D數組,或(2)為每個字符串分配存儲並將該字符串復制到新的內存塊中,並將指針指向該塊的開頭(例如s1[x] )。 strdup函數在單個函數調用中提供分配和復制 如果沒有strdup ,這是使用strlenmallocmemcopy編寫的簡單函數(如果字符串可能重疊,請使用memmove )。

一旦確定了需要保留的值以供以后在代碼中使用,就可以確保聲明的變量的作用域正確,並且可以確保每個變量都已正確初始化並分配了存儲空間,剩下的就是編寫邏輯來進行處理按預期工作。

在轉向示例之前,值得注意的是,您對數據的面向行輸入感興趣。 scanf系列提供了格式化的輸入 ,但是通常最好將面向行的函數用於實際輸入(例如fgets ),然后使用sscanf進行單獨的解析。 在這種情況下,由於您的第一個值是字符串值 ,並且%s格式說明符將跳過中間的空格 ,因此在很大程度上是洗手間,但通常並非如此。 例如,您正在有效地閱讀:

    char tmp1[MAXC] = "", tmp2[MAXC] = "";
    ...
    if (fscanf (fp, "%s %s %d", tmp1, tmp2, &n) != 3)
        break;

可以輕松地將其替換為更強大的功能:

    char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
    ...
    if (!fgets (buf, sizeof buf, fp) ||
        sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
        break;

(這將分別驗證讀取和轉換)

上面,還要注意使用臨時緩沖區tmp1tmp2 (以及buffgets一起使用),將輸入讀入可以在最終存儲之前進行驗證以供以后使用的臨時值中是非常有利的。

剩下的就是簡單地將各個部分按正確的順序組合在一起以實現您的目標。 下面是一個示例,該示例從作為第一個參數給出的文件名中讀取數據(如果沒有給出文件名,則從stdin中讀取數據),然后輸出數據並釋放所有分配的內存,然后退出。

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

#define MAXC 20

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

    char *s1[MAXC], *t1[MAXC],
        *production_left[MAXC], *production_right[MAXC];
    int idx[MAXC] = {0},              /* storage for 'n' values */
        iss = 0, ipp = 0, pidx = 0;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* loop until no header row read, protecting array bounds */
    for (; ipp < MAXC && iss < MAXC; iss++) {
        char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
        int n = 0;

        if (!fgets (buf, sizeof buf, fp) ||
            sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
            break;

        idx[iss] = n;
        s1[iss] = strdup (tmp1);    /* strdup - allocate & copy */
        t1[iss] = strdup (tmp2);

        if (!s1[iss] || !t1[iss]) { /* if either is NULL, handle error */
            fprintf (stderr, "error: s1 or s1 empty/NULL, iss: %d\n", iss);
            return 1;
        }

        /* read 'n' data lines from file, protecting array bounds */
        for (int i = 0; i < n && ipp < MAXC; i++, ipp++) {
            char ptmp1[MAXC] = "", ptmp2[MAXC] = "";

            if (!fgets (buf, sizeof buf, fp) ||
                sscanf (buf, "%s %s", ptmp1, ptmp2) != 2) {
                fprintf (stderr, "error: read failure, ipp: %d\n", iss);
                return 1;
            }
            production_left[ipp] = strdup (ptmp1);
            production_right[ipp] = strdup (ptmp2);

            if (!production_left[ipp] || !production_right[ipp]) {
                fprintf (stderr, "error: production_left or "
                        "production_right empty/NULL, iss: %d\n", iss);
                return 1;
            }
        }
    }

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    for (int i = 0; i < iss; i++) {
        printf ("%-8s %-8s  %2d\n", s1[i], t1[i], idx[i]);
        free (s1[i]);  /* free s & t allocations */
        free (t1[i]);
        for (int j = pidx; j < pidx + idx[i]; j++) {
            printf ("  %-8s %-8s\n", production_left[j], production_right[j]);
            free (production_left[j]);  /* free production allocations */
            free (production_right[j]);
        }
        pidx += idx[i];  /* increment previous index value */
    }

    return 0;
}

輸入文件示例

$ cat dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B

使用/輸出示例

$ ./bin/production <dat/production.txt
AA       BB         4
  A        B
  AB       BA
  AA       CC
  CC       BB
A        B          3
  A        C
  B        C
  c        B

內存使用/錯誤檢查

在您編寫的任何可以動態分配內存的代碼中,對於任何分配的內存塊,您都有2個責任 :(1) 始終保留指向該內存塊起始地址的指針,因此,(2)在沒有內存塊時可以將其釋放需要更長的時間。

必須使用一個內存錯誤檢查程序來確保您不會嘗試在分配的內存塊的邊界之外/之外進行寫操作,不要嘗試在未初始化的值上讀取或基於條件跳轉,最后確認您釋放所有已分配的內存。

對於Linux, valgrind是通常的選擇。 每個平台都有類似的內存檢查器。 它們都很容易使用,只需通過它運行程序即可。

$ valgrind ./bin/production <dat/production.txt
==3946== Memcheck, a memory error detector
==3946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3946== Command: ./bin/production
==3946==
AA       BB         4
  A        B
  AB       BA
  AA       CC
  CC       BB
A        B          3
  A        C
  B        C
  c        B
==3946==
==3946== HEAP SUMMARY:
==3946==     in use at exit: 0 bytes in 0 blocks
==3946==   total heap usage: 18 allocs, 18 frees, 44 bytes allocated
==3946==
==3946== All heap blocks were freed -- no leaks are possible
==3946==
==3946== For counts of detected and suppressed errors, rerun with: -v
==3946== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始終確認已釋放已分配的所有內存,並且沒有內存錯誤。

仔細檢查一下,如果您還有其他問題,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM