2

私はCの初心者で、3つの要素を持つ基本的なジェネリックリンクリストを実装しようとしています。各要素には、異なるデータ型の値— intcharおよびが含まれますdouble

これが私のコードです:

#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;
}

最初の2つの要素に問題はありませんが、double型の値を3番目の要素に割り当てようとすると、次のエラーが発生します。«から変換できませんdoubledouble *»。

このエラーの理由は何ですか?intまたはの場合に同じエラーが発生しないのはなぜcharですか?そして最も重要な質問:これを修正する方法double、3番目の要素のデータフィールドに値を割り当てる方法は?

問題の文字列は« (double*)third->data = 5.6;»です。

ありがとう。

4

3 に答える 3

6

「実際の」例では、malloc新しく割り当てられたスペースへのポインターを取得するために呼び出し、すぐにそのポインターを破棄して、ポインター値を整数値または文字値に置き換えます。ほとんどのC実装では、警告が表示されるはずですが、ポインタセルは整数値またはchar値を保持できるため、これは多かれ少なかれ偶然に機能します。これらの割り当ての後で実際にデータポインタを逆参照しようとすると、クラッシュとコアダンプが発生する可能性があります。

ポインタ自体ではなく、ポインタが指す場所に値を配置する必要があります。つまり、追加が必要です*

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

型キャストのは*(double *)型名の一部です-「doubleへのポインタ」。キャストは「の値を取り、third->dataそれをdoubleへのポインタとして解釈する」と言っています。結果はまだポインタであるため、それに割り当てると、ポインタが指す場所が変更されます(おそらく、ポインタが意味のない場所を指すようになります)。代わりに、すでに指している場所に値を割り当てたいと思います。これは、アウター*が行うことです。

ただし、、、、など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;
于 2012-11-03T22:18:16.450 に答える
2

ポインターをキャストしていますが、割り当てのためにそれを参照解除する必要があります。割り当ては、ポインターにキャストされてから最初の2つで機能し、次のintようcharになります。

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

とにかく、そもそもそのようなキャストには警告があるはずです。

于 2012-11-03T22:22:14.857 に答える
1

ポインタに値を割り当てることはできません。ポイントされたオブジェクトに値を割り当てる必要があります(最後の場合、double-あなたは1つのdoubleのためのスペースしかありません)。

それで:

    ...
    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

ところで:完全な警告が有効になっていると、コンパイラは最初の2つの割り当てが危険である(GCCはそれらをエラーと見なす)と3番目の割り当ては許可されない(doubleからポインタに変換できない)ことを警告する必要があります

もう1つ、このようにstructペイロードを使用する場合、ペイロード自体に実際に格納したデータ型が失われることを考慮する必要があります。したがって、リンクリストのインスタンスを調べても、それがchar、integer、doubleのいずれであるかを判断することはできません。さらに悪いことに、値をチェックすることさえ許可されず、プログラムがクラッシュする可能性があります(1バイトを格納したが、4または8を読み取ろうとしたとします)。

したがって、元のデータ型のインジケーター(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;
}
于 2012-11-03T22:21:42.130 に答える