2013-11-04 136 views
0

搜索了一個小時左右。我想我最好在這裏發佈這個問題。爲什麼初始化結構時會出現段錯誤?

我簡化了代碼。段錯誤在函數initMyStruct中。

#include "stdlib.h" 

typedef struct { 
     int * arr1; 
     int * arr2; 
} myStruct; 

void allocMyStruct (myStruct * a, int num) { 
     a = malloc(sizeof(myStruct)); 
     a->arr1 = malloc(10*sizeof(int)); 
     a->arr2 = malloc(10*num*sizeof(int)); 
} 
void initMyStruct (myStruct * a, int num) { 
     int i; 
     for (i = 0; i < 10; i++)  a->arr1[i] = 0; 
     for (i = 0; i < 10*num; i++) a->arr2[i] = -1; 
} 
void freeMyStruct (myStruct * a, int num) { 
     int i; 
     for (i = 0; i < 10; i++)  free(a->arr1); 
     for (i = 0; i < 10*num; i++) free(a->arr2); 
     free(a); 
} 
int main (void) { 
     int num = 3; 
     myStruct * a; 
     allocMyStruct (a, num); 
     initMyStruct (a, num); 
     freeMyStruct (a, num); 
     return 1; 
} 
+1

無關,但爲什麼你的'main'函數返回1?如果程序返回1,則表示某種AFAIK失敗。 std lib有宏的定義:'#define EXIT_SUCCESS 0'和'#define EXIT_FAILURE 1' ... –

回答

4

因爲你不保留指向新分配的內存的指針,而是使用未初始化的指針並獲取未定義的行爲。

您傳遞a可變進allocMyStruct(),但電話是(所有其他人一樣)按值,因此新值裏面的功能確實影響amain()值分配給它。

更改它使allocMyStruct()要麼返回新的指針值,要麼將指針指向指針。我寧願是前者,它的清潔,並使用函數返回值往往會導致更好的代碼:

myStruct * allocMyStruct(int num) 
{ 
    myStruct *p; 

    if((p = malloc(sizeof *p + 
       10 * sizeof *p->arr1 + 
       10 * num * sizeof *p->arr2)) != NULL) 
    { 
    p->arr1 = (int *) (p + 1); 
    p->arr2 = p->arr1 + 10; 
    } 
    return p; 
} 

上面的代碼也簡化了內存分配,做這一切在一個大malloc()調用,它就是被「切」成你實際需要的三個部分。

如果arr1的大小總是爲10,那麼動態分配它就沒有意義了,它在結構聲明中應該只是int arr1[10];

+1

+1爲優化的'malloc'調用我還在等待我的C語言的編程語言_,所以請原諒我的問題:是「p + 1」總是正確的,還是應該是「p + sizeof * void」或什麼的? –

+1

你怎麼知道'(int *)(p + 1)'對齊了'p-> arr1'? –

+1

@AlterMann:由於在這種情況下struct只包含'int *',所以在這裏可能不需要任何結構對齊。但也許值得一提的是,假設'p-> arr1'將會在'p + 1',結構增長的那一刻以及添加新類型的風險,我懷疑可能會有麻煩,除非數量明確指定每個成員的位數...或者我只是偏執狂? –

1

a用於初始化,更改爲:

myStruct * allocMyStruct (int num) { 
     myStruct *a; 

     a = malloc(sizeof(myStruct)); 
     a->arr1 = malloc(10*sizeof(int)); 
     a->arr2 = malloc(10*num*sizeof(int)); 
     return a; 
} 
myStruct * a = allocMyStruct(num); 

而且,也沒有必要到循環在你的自由功能

void freeMyStruct (myStruct * a, int num) { 
     int i; 
     for (i = 0; i < 10; i++)  free(a->arr1); 
     for (i = 0; i < 10*num; i++) free(a->arr2); 
     free(a); 
} 

必須

void freeMyStruct (myStruct * a) { 
     free(a->arr1); 
     free(a->arr2); 
     free(a); 
} 
1

當您打電話void allocMyStruct (myStruct * a, int num)a指針將作爲值傳遞並且a參數是您的指針main的本地副本,在您更改三個函數中的任意一箇中的本地a之後,它將不會在main中更改。

爲此,您必須使用雙指針作爲函數參數,因此這些函數將獲取指針的地址,以便它們可以對其進行修改。

#include "stdlib.h" 

typedef struct { 
     int * arr1; 
     int * arr2; 
} myStruct; 

void allocMyStruct (myStruct ** a, int num) { 
     *a = malloc(sizeof(myStruct)); 
     (*a)->arr1 = malloc(10*sizeof(int)); 
     (*a)->arr2 = malloc(10*num*sizeof(int)); 
} 
void initMyStruct (myStruct ** a, int num) { 
     int i; 
     for (i = 0; i < 10; i++)  (*a)->arr1[i] = 0; 
     for (i = 0; i < 10*num; i++) (*a)->arr2[i] = -1; 
} 
void freeMyStruct (myStruct ** a, int num) { 
     free((*a)->arr1); 
     free((*a)->arr2); 
     free(*a); 
     *a = NULL; 
} 
int main (void) { 
     int num = 3; 
     myStruct * a; 
     allocMyStruct (&a, num); 
     initMyStruct (&a, num); 
     freeMyStruct (&a, num); 
     return 1; 
} 

編輯:阿爾特·曼是正確的關於同一地址的多個釋放,在Linux上,你會得到瞬間崩潰雙釋放。他有一個更簡單的解決方案。