我必须建立一个链表,每个节点是一个16字节的数组。使用与单个uint8_t值一起工作的代码,我稍微修改了它以接受16字节。但是,无论何时打印列表,所有节点都是最后添加的节点。我做错了什么?
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<math.h>
struct array_deck
{
uint8_t *val;
struct array_deck *next;
};
struct array_deck *head = NULL;
struct array_deck *curr = NULL;
int sizeOfSet;
struct array_deck *create_list(uint8_t *val)
{
struct array_deck *ptr = (struct array_deck*)malloc(sizeof(struct array_deck));
if(NULL == ptr)
return NULL;
ptr->val = val;
ptr->next = NULL;
head = curr = ptr;
return ptr;
}
void print_list(void)
{
struct array_deck *ptr = head;
while(ptr != NULL)
{
printf("n");
printf("actually added = ");
for (int i = 0; i < 16; i++) {
uint8_t blah = ptr->val[i];
printf("[%2i] ",blah);
}
ptr = ptr->next;
}
printf("n");
return;
}
struct array_deck *add_to_list(uint8_t *data, bool add_to_end)
{
printf("array to be added = ");
for (int i = 0; i < 16; i++) {
printf("[%2X] ",data[i]);
}
if(NULL == head)
return (create_list(data));
struct array_deck *ptr = (struct array_deck*)malloc(sizeof(struct array_deck));
if(NULL == ptr)
return NULL;
ptr->val = data;
ptr->next = NULL;
if(add_to_end)
{
curr->next = ptr;
curr = ptr;
}
else
{
ptr->next = head;
head = ptr;
}
return ptr;
}
int main(void)
{
printf ("# of arrays >> ");
scanf ("%d", &sizeOfSet);
uint8_t data[16];
for(int i = 1; i<=sizeOfSet; i++){
for (int j = 0; j < 16; j++) {
data[j] = i;
}
printf("n");
add_to_list(data,true);
}
print_list();
return 0;
}
构建3个节点的链表的示例运行:
# of arrays >> 3
array to be added = [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1] [ 1]
array to be added = [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2] [ 2]
array to be added = [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3]
actually added = [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3]
actually added = [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3]
actually added = [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3] [ 3]
Program ended with exit code: 0
所有的array_deck
's -> val都指向相同的内存地址 -指向main中的本地变量data
。你重写了N次,这就是为什么它打印最后插入的值。
要解决这个问题,您应该将 data
数组复制到new中,并将新分配的数组地址分配给ptr->val
。
struct array_deck *add_to_list(uint8_t *data, bool add_to_end)
{
...
struct array_deck *ptr = (struct array_deck*)malloc(sizeof(struct array_deck));
if(NULL == ptr)
return NULL;
ptr->val = malloc(16*sizeof(uint8_t));
memcpy(ptr->val, data, 16*sizeof(uint8_t));
ptr->next = NULL;
...
}
注:删除 create_list函数并在add_to_list中执行分配给head的所有逻辑。
在create_list
和add_to_list
中,节点数据没有被正确复制。所示的代码只是存储一个指向传入数据的指针,而不是创建数据的副本。因此,结果是所有节点最终都指向main
中定义的相同data
,因此在打印时产生相同的值。
有很多方法可以解决这个问题。有些比其他更优雅,但都需要复制数据。
最简单的方法是假设固定大小的数据,并为数据声明带有静态内存的节点结构。像这样:#define DATA_BYTES 16
struct array_deck
{
uint8_t val[DATA_BYTES];
struct array_deck *next;
};
然后用数据副本替换列表函数中的指针赋值。例如:
memcpy(ptr->val, val, sizeof(ptr->val));
一个更灵活/优雅的解决方案是显式地将数据的大小存储在节点本身中,这样您就可以拥有可变大小的数据。然后使用动态内存分配(malloc
和朋友)为节点val
获取内存。