2012-11-03 106 views
2

我在C中很新手,現在我試圖用3個元素實現基本的通用鏈表,每個元素將包含不同的數據類型值 - int,chardouble從雙指針轉換爲

這裏是我的代碼:

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

struct node 
{ 
    void* data; 
    struct node* next; 
}; 

struct node* BuildOneTwoThree() 
{ 
    struct node* head = NULL; 
    struct node* second = NULL; 
    struct node* third = NULL; 

    head = (struct node*)malloc(sizeof(struct node)); 
    second = (struct node*)malloc(sizeof(struct node)); 
    third = (struct node*)malloc(sizeof(struct node)); 

    head->data = (int*)malloc(sizeof(int)); 
    (int*)(head->data) = 2; 
    head->next = second; 

    second->data = (char*)malloc(sizeof(char)); 
    (char*)second->data = 'b'; 
    second->next = third; 

    third->data = (double*)malloc(sizeof(double)); 
    (double*)third->data = 5.6; 
    third->next = NULL; 

    return head; 
} 

int main(void) 
{ 
    struct node* lst = BuildOneTwoThree(); 

    printf("%d\n", lst->data); 
    printf("%c\n", lst->next->data); 
    printf("%.2f\n", lst->next->next->data); 

    return 0; 
} 

我與前兩個元素沒有問題,但是當我嘗試分配雙到第三元素類型的值,我得到一個錯誤:«不能從double轉換爲double *»。

這個錯誤的原因是什麼?爲什麼在intchar的情況下我不會得到相同的錯誤? 而最重要的問題是:如何解決這個問題,如何爲第三個元素的數據字段賦值double

問題字符串是«(double*)third->data = 5.6;»。

謝謝。

回答

2

您是鑄造的指針,但你需要derefernce它分配,分配工作了前兩個,因爲intchar被鑄造爲指針,它應該是:

*((int*)(head->data)) = 2; 
*((char*)(second->data)) = 'b'; 
*((double*)(third->data)) = 5.6; 

總之,應該有預警的第一名鑄造。

+0

我仍然說使用指針來存儲基元類型是愚蠢的。 –

6

在你的「工作」的例子,你打電話malloc獲取指向一些新分配的空間,然後立即拋出該指針離開和更換指針值與整型或字符值。這或多或少是偶然的,因爲在大多數C實現中,指針單元格可以包含整數或char值,儘管您應該會收到警告。如果實際嘗試在這些賦值之後取消引用數據指針,則可能會發生崩潰和核心轉儲。

您希望將指針指向的位置的值,而不是指針本身。這意味着你需要一個額外的*

*((double *)third->data) = 5.6; 

*(double *)類型轉換的類型是名稱的一部分 - 「指針翻一番」。演員表示「取third->data的值,並將其解釋爲雙倍指針」。結果仍然是一個指針,所以當你指定它的時候,你正在改變指針指向的位置(並且可能使它指向某處沒有意義)。相反,你想爲它已經指向的地方分配一個值,這就是外層*所做的。

但是,如果您只存儲基本類型,如int,chardouble,則不需要經過指針(並且擔心伴隨的內存管理)。你可以只用一個聯盟:

struct node 
{ 
    struct node *next; 
    union { 
     char c; 
     int i; 
     double d; 
    } data; 
} 

然後你會做如

head->data.i = 2; 
second->data.c = 'b'; 
third->data.d = 5.6; 
1

您不能分配值到指針,你必須把它分配給對象指向(在後一種情況下,雙 - 你只需要空間一個雙)。

所以:

... 
    head->data = (int*)malloc(sizeof(int)); 
    ((int*)(head->data))[0] = 2; 
    head->next = second; 

    second->data = (char*)malloc(sizeof(char)); 
    ((char*)second->data)[0] = 'b'; 
    second->next = third; 

    third->data = (double*)malloc(2 * sizeof(double)); 
    ((double*)third->data)[0] = 5.6; 
    ((double*)third->data)[1] = 3.1415; 
    // We only allocated space for 2 doubles, so this line here would cause a crash 
    // (or anyway, a data corruption) 
    // ((double*)third->data)[2] = 666; 
    third->next = NULL; 

    return head; 
} 

int main(void) 
{ 
    struct node* lst = BuildOneTwoThree(); 

    printf("%d\n", ((int *)lst->data)[0]); 
    printf("%c\n", ((char *)lst->next->data)[0]); 
    printf("%.2f\n", ((double *)lst->next->next->data)[0]); 
    printf("%.2f\n", ((double *)lst->next->next->data)[1]); 
    ... 

回報:

2 
b 
5.60 
3.14 

BTW:啓用全警告,編譯器應該提醒你的是,前兩個任務是危險的(GCC認爲這些錯誤)和第三個不允許(不能從雙精度轉換爲指針)

還有一件事:當你用這種方式使用結構有效載荷時,你必須考慮你實際使用的數據類型存儲在有效載荷本身丟失。所以你不能通過檢查鏈表的實例來確定它是char,整數還是double。更糟糕的是,即使檢查值可能不被允許和程序崩潰(假設你存儲一個字節,但嘗試讀取四個或八個)。

所以,你應該還可以存儲一個額外的字段在你的結構保持原始數據類型的指標(一enum也許):

typedef enum 
{ 
    TYPE_IS_CHAR, 
    TYPE_IS_INT, 
    TYPE_IS_FLOAT, 
    TYPE_IS_DOUBLE, 
    ... 
} mytype_t; 

struct node 
{ 
    mytype_t type; 
    void  *data; 
    struct node *next; 
}