我在C中相当新手,现在我正在尝试使用3个元素实现基本通用链表,每个元素将包含不同的数据类型值 - int
,char
和{{1 }}
这是我的代码:
double
我对前两个元素没有问题,但是当我尝试将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;
}
转换为{{1 }} »。
出现此错误的原因是什么?为什么double
或double *
出现同样的错误?
最重要的问题:如何解决这个问题,如何将int
值分配给第三个元素的数据字段?
问题字符串是«char
»。
感谢。
答案 0 :(得分:6)
在“工作”示例中,您正在调用malloc
来获取指向某个新分配空间的指针,然后立即抛出该指针并将指针值替换为你的整数或字符值。这或多或少是偶然的,因为在大多数C实现中,指针单元格可以包含整数或char值,但是您应该收到警告。如果您在这些分配后实际尝试取消引用数据指针,则可能会发生崩溃和核心转储。
您希望将值放在指针指向的位置,而不是指针本身。这意味着您需要额外的*
:
*((double *)third->data) = 5.6;
*
类型转换中的(double *)
是类型名称的一部分 - “指向double的指针”。演员表示“取third->data
的值并将其解释为指向双倍的指针”。结果仍然是一个指针,所以当你指定它时,你正在改变指针指向的位置(并且可能使它指向某个地方毫无意义)。相反,您希望为已经指向的位置指定一个值,这就是外部*
所做的。
但是,如果您只存储int
,char
和double
等基本类型,则无需通过指针(并担心伴随的内存)管理)。你可以使用一个联盟:
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 :(得分:2)
你正在施放指针,但是你需要为指派做出决定,因为int
和char
已被转换为指针,因此前两个指派工作正常,它应该是:
*((int*)(head->data)) = 2;
*((char*)(second->data)) = 'b';
*((double*)(third->data)) = 5.6;
无论如何,首先应该警告这样的演员。
答案 2 :(得分: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认为它们是错误)而第三个分配是不允许的(不能从双精度转换为指针)
还有一件事:当您以这种方式使用struct有效负载时,您必须考虑实际存储在有效负载本身中的数据类型。因此,您不能通过检查链接列表的实例来确定它是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;
}