所以我开始研究C中的数据结构,并想编写一个Singly Linked List。
这是其中的一小部分:
struct nodes {
int val;
struct nodes *next;
};
void insert(struct nodes *list, int val) {
struct nodes *tmp = (struct nodes*) malloc(sizeof(struct nodes));
tmp->val = val;
tmp->next = list;
list = tmp;
}
int main() {
struct nodes *test;
insert(test, 5);
insert(test, 10);
printf("test %dn", test->next->val);
}
这里我得到了完全错误的输出。当我尝试编写一个不必传入结构指针的函数时,它的工作原理与预期的一样。
输出:
test 90053
p.S我仍然从C开始,所以不要判断得太难:D
您正在更改一个局部变量,这样就不会对调用方产生影响。你的意思可能是使用一个指针指向一个可以更改的指针:
int main() {
struct nodes *test = NULL; // Don't forget to initialize
insert(&test, 5);
insert(&test, 10);
printf("test %dn", test->next->val);
}
你的功能现在看起来像什么:
void insert(struct nodes **list, int val) {
struct nodes *tmp = malloc(sizeof(struct nodes)); // No need to cast malloc() in C
tmp->val = val;
tmp->next = *list;
*list = tmp;
}
由于list
被取消引用(*list
(,它实际上会更改原始值(指向的值(,这意味着您的列表头会更改。
在您的原始代码中,您从未初始化过list
,它只是一些随机的垃圾值,并且由于insert
永远无法更改它,因此它将保留为垃圾数据。通过它访问任何数据都是未定义的行为,所以你会得到垃圾数据或崩溃。
你可以走这条路:
struct nodes {
int val;
struct nodes* next;
};
nodes* insert(struct nodes* list, int val) {
struct nodes* tmp = (struct nodes*)malloc(sizeof(struct nodes));
tmp->val = val;
tmp->next = list;
return tmp;
}
当尝试这样的结构时,你写的第一个函数——我相信——是list((而不是insert((。
在每个可以更改列表起始地址的函数中,必须返回地址,否则地址将丢失。由于你只在开头插入,你就失去了一切,因为起始地址总是变化的。。一般情况下,不要使用void
。至少是一种浪费,通常是一种错误。返回一些东西,比如完成状态。请参阅list()
的实现,该实现返回。。。size
int list(struct nodes* list)
{
struct nodes* p = list;
int N = 0;
while (p != NULL)
{
printf("#%d %dn", N, p->val);
p = p->next; N += 1;
};
printf("n");
return N;
}
和使用2个函数的main((版本
int main() {
struct nodes* test = NULL;
int n = 0;
for(n = 800; n>0; n-= 100) test = insert(test, n);
n = list(test);
printf("At the end list() returned %dn", n);
}
通过这种方式,您可以从一开始就获得更多信息。
#0 100
#1 200
#2 300
#3 400
#4 500
#5 600
#6 700
#7 800
At the end list() returned 8
测试程序
#include <stdio.h>
#include <stdlib.h>
struct no
{
void* item;
struct no* next;
struct no* prev;
}; // no
typedef struct no Node;
typedef struct
{
char* name;
unsigned size;
unsigned limit;
Node* start;
Node* end;
} List;
struct nodes {
int val;
struct nodes* next;
};
struct nodes* insert(struct nodes*, int);
int list(struct nodes* L);
int main() {
struct nodes* test = NULL;
int n = 0;
for(n = 800; n>0; n-= 100) test = insert(test, n);
n = list(test);
printf("At the end list() returned %dn", n);
}
struct nodes* insert(struct nodes* list, int val) {
struct nodes* tmp = (struct nodes*)malloc(sizeof(struct nodes));
tmp->val = val;
tmp->next = list;
return tmp;
}
int list(struct nodes* list)
{
struct nodes* p = list;
int N = 0;
while (p != NULL)
{
printf("#%d %dn", N, p->val);
p = p->next; N += 1;
};
printf("n");
return N;
}
正如评论中所指出的,请注意,列表不是node
。一个列表,如果是节点的集合,并且节点具有数据负载。如果有效载荷是(void*)
,那么您就有了一个真正抽象的数据结构,因为它可以加载任何内容,从单个char
到100-fields structure
。
想想你写它的方式,以及你的程序中有5个列表的情况:它很快就会变成一团糟。并与下面的代码进行比较
链表结构
struct no
{
void* item;
struct no* next;
struct no* prev;
}; // no
typedef struct no Node;
typedef struct
{
char* name;
unsigned size;
unsigned limit;
Node* start;
Node* end;
} List;
注意,当你在List
中输入更多信息时,事情会变得更容易:你总是有一个大小、一个限制、开始和结束的指针,等等