簡體   English   中英

C - 分段錯誤讀取結構和 Dynamic2D 數組成員

[英]C - Segmentation Fault Reading Struct and Dynamic2D Array Member

我剛開始學習 C,我想嘗試創建一個適用於指針、結構和 arrays 的測試程序,因為我仍然很難理解它們。 我創建了這個測試文件,它是我正在處理的一個更大項目的精煉版本。 測試文件有一個結構體,其中包含一個動態二維數組作為結構體的成員:

typedef struct {
  int ** array;
  int rows, cols;
} Smaller;

但是,運行測試文件后,終端返回以下錯誤:

zsh: segmentation fault ./a.out

我研究了這個錯誤的含義,

" 分段錯誤是訪問“不屬於您”的 memory 導致的一種特定錯誤。 鏈接

但我仍然對如何解決這個問題感到困惑。 我很確定我為每一行和每一列分配了正確數量的 memory。 這更加令人困惑,因為終端沒有指示錯誤是哪一行。 我將不勝感激有關此問題的任何幫助。

以下是完整代碼:

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

typedef struct {
  int ** array;
  int rows, cols;
} Smaller;


void printArray (Smaller * s);

int main () {
  int x, i, j;

  Smaller * sand;

  // allocate mem for number of rows
  sand->array = malloc (3 * sizeof(int *));

  //allocate mem for number of columns
  sand->array = malloc(4 * sizeof(int));
  sand->array = malloc(4 * sizeof(int));
  sand->array = malloc(4 * sizeof(int));

  // adding a constant value to the 2D array
  for (i = 0; i < 3; i ++) {
    for (j = 0; j < 4; j ++) {
      sand->array[i][j] = 6;
    }
  }

  printArray(sand);

  return 0;
}

void printArray (Smaller * sand) {
  printf("Welcome to the printArray function! \n");

  int i, j;

  for (i = 0; i < 3; i ++)
    for(j = 0; j < 4; j ++)
      printf("array[%d][%d] = %d \n", i, j, sand->array[i][j]);

}

問題是,正如@tromgy 指出的那樣,您正在用列 arrays 覆蓋基礎 sand- sand->array而不是將它們分配給它。 正確的代碼如下所示:

#include <stdlib.h>
#define NUM_ROWS 3
#define NUM_COLS 4

typedef struct {
    int ** array;
    int rows;
    int cols;
} Smaller;

void print_array(Smaller * s);

int main(void) {
    Smaller * sand = malloc(sizeof(Smaller));
    if (!sand) return -1; /* allocation failed, abort */
    sand->rows = NUM_ROWS;
    sand->array = malloc(sizeof(int*[NUM_ROWS]));
    if (!sand->array) { /* allocation failed, abort */
        free(sand); /* free sand first, though */
        return -1;
    }
    for (size_t i = 0; i < NUM_ROWS; ++i) {
        sand->array[i] = malloc(sizeof(int[NUM_COLS]));
        if (!sand->array[i]) {
            /* free the previous rows */
            for (size_t j = 0; j < i; ++j) free(sand->array[j]);
            free(sand->array);
            free(sand);
            return -1;
        }
    }
    /* add a constant value to the array */
    for (size_t i = 0; i < NUM_ROWS; ++i) {
        for (size_t j = 0; j < NUM_COLS; j ++) {
            sand->array[i][j] = 6;
        }
    }
    print_array(sand);
    /* Ok, now free everything */
    for (size_t i = 0; i < NUM_COLS; ++i) {
        free(sand->array[i]);
    }
    free(sand->array);
    free(sand);
    /* NOW we may exit */
    return 0;
}

如您所見,分配這樣的結構需要大量工作,並且您必須釋放分配的任何內容,因此最好將其提取到 function 中,例如Smaller * smaller_init(size_t nrows, size_t ncols)void smaller_destroy(Smaller * s)封裝了所有的工作。

我將在下面留下一個示例,以便您可以將其與您最初編寫它的方式進行比較...

關於您的代碼:

  • for命令中聲明循環變量
  • 可能Smaller不需要是指針
  • 將尺寸保留為變量。 它更靈活
  • 您沒有在struct中設置rowscols的值。 main()中不要像你那樣使用固定值 3 和 4
  • 您應該將所有單元格設置為不同的值,而不是相同的值。 當您看到可逆值時,您會感覺更安全,例如示例中的 100* row + column ...這樣您可以查看循環是否正常並且所有元素都正在打印。 請參閱此 output 的 printArray printArray()
      0    1    2    3
    100  101  102  103
    200  201  202  203

每行都以行號開頭,因此您可以在繼續之前對其進行幾次測試。

  • 讓您的程序自行測試。 例如,在printArray()中顯示如下尺寸:
    printArray[3,4]

      0    1    2    3
    100  101  102  103
    200  201  202  203

參見示例的 output

  • 始終編寫代碼以釋放 memory,按照分配的保留順序,可能在單獨的 function 中返回 NULL 以使調用代碼中的指針無效,就像這樣
Smaller* freeArray(Smaller* A)
{
    printf("\nfreeArray()\n");
    for (int i = 0; i < A->rows; i++)
    {
        free(A->array[i]);  // delete lines
        printf("row %d free()\n", i);
    }
    free(A->array);  // delete cols
    printf("pointer to rows free()\n");
    free(A);  // delete struct
    printf("struct free()\n");
    return NULL;
}

這樣你就知道指針sand不會指向一個已經free() d 的區域。 使用這樣的指針會使你的程序崩潰,所以最好寫

    sand = freeArray(sand);

示例代碼的 output

printArray[3,4]

  0    1    2    3
100  101  102  103
200  201  202  203

freeArray()
row 0 free()
row 1 free()
row 2 free()
pointer to rows free()
struct free()

示例代碼

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

typedef struct
{
    int** array;
    int   rows, cols;

} Smaller;

void     fillArray(Smaller*);
Smaller* freeArray(Smaller*);
Smaller* makeArray(size_t, size_t);
void     printArray(Smaller*);

int main(void)
{
    int y = 3; 
    int x = 4;
    // sand points to a Smaller
    Smaller* sand = makeArray(y, x);
    // adding known unique values to cells is easier
    fillArray(sand);
    printArray(sand); // show values
    sand = freeArray(sand); // delete all
    return 0;
}

void fillArray(Smaller* A)
{
    for (int i = 0; i < A->rows; i++)
        for (int j = 0; j < A->cols; j++)
            A->array[i][j] = 100 * i + j;
}

Smaller* freeArray(Smaller* A)
{
    printf("\nfreeArray()\n");
    for (int i = 0; i < A->rows; i++)
    {
        free(A->array[i]);  // delete lines
        printf("row %d free()\n", i);
    }
    free(A->array);  // delete cols
    printf("pointer to rows free()\n");
    free(A);  // delete struct
    printf("struct free()\n");
    return NULL;
}

Smaller* makeArray(size_t y, size_t x)
{
    // sand points to a Smaller
    Smaller* sand = (Smaller*)malloc(sizeof(Smaller));
    sand->rows    = y;
    sand->cols    = x;

    // allocate mem for number of rows, that is 'y'
    sand->array = malloc(y * sizeof(int*));

    // allocate mem for each of the 'x' columns
    for (size_t i = 0; i < y; i++)
        sand->array[i] = malloc(x * sizeof(int));

    return sand;
};

void printArray(Smaller* sand)
{
    printf("printArray[%d,%d]\n\n", sand->rows, sand->cols);
    for (int i = 0; i < sand->rows; i++)
    {
        for (int j = 0; j < sand->cols; j++)
            printf("%3d  ", sand->array[i][j]);
        printf("\n");
    }
}

關於代碼

請所以人們不要費心指出我不要投射malloc()的結果。 這是由決定。 這個常見的建議是對 90 年代 C-faq 的回憶,現在我們知道隱式轉換可能不太好。 事實上,隱含的事情可能會花費你很多時間:如果你malloc()程序中的一系列不同結構並省略類型,如果其中一些是相反的,請記住,使用所有強制轉換將幫助你避免這種代價高昂的錯誤...

暫無
暫無

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

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