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 类型的值分配给第三个元素时,我得到一个错误:« can not convert from doubletodouble * »。

这个错误的原因是什么?为什么在intor的情况下我不会得到相同的错误char?还有最重要的问题:如何解决这个问题,如何为double第三个元素的数据字段赋值?

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

谢谢。

4

3 回答 3

6

在您的“工作”示例中,您调用malloc以获取指向某个新分配空间的指针,然后立即将该指针丢弃并用您的整数或字符值替换指针值。这或多或少是偶然的,因为在大多数 C 实现中,指针单元格可以保存整数或字符值,尽管您应该收到警告。如果您真的尝试在这些分配之后取消引用数据指针,您可能会遇到崩溃和核心转储。

您希望将值放在指针指向的位置而不是指针本身。这意味着您需要额外的*

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

*类型转换中的(double *)是类型名称的一部分 - “指向双精度的指针”。演员表说“取值third->data并将其解释为指向双精度的指针”。结果仍然是一个指针,因此当您分配给它时,您正在更改指针指向的位置(并且可能使其指向某个毫无意义的地方)。相反,您想为它已经指向的位置分配一个值,这就是外部*所做的。

但是,如果您只存储基本类型,如intchardouble,则不需要通过指针(并担心随之而来的内存管理)。你可以只使用一个联合:

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

您正在转换指针,但您需要取消对分配的引用,该分配适用于前两个,因为intandchar被转换为指针,它应该是:

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

无论如何,首先应该对这样的演员发出警告。

于 2012-11-03T22:22:14.857 回答
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

顺便说一句:启用完整警告后,编译器应该警告您前两个分配是有风险的(GCC 认为它们是错误的),第三个是不允许的(不能从双精度转换为指针)

还有一件事:当您以这种方式使用结构负载时,您必须考虑实际存储在负载本身中的数据类型会丢失。因此,您不能通过检查链表的实例来确定它是 char、integer 还是 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;
}
于 2012-11-03T22:21:42.130 回答