由于这是我直接从Notion复制过来的,很多格式有问题,建议直接去我的Notion里面看,链接https://billowy-pigment-2ec.notion.site/8cc78481ec66478e90cad44e2bc05db9?pvs=4
写的比较粗糙,凑合看吧
但是个人在后面的链表实现问题写得比较详细
同时也欢迎各位佬来提意见!
-
开发日志(
抱怨日记)-
10.2X 开始写屎山,喜欢往NULL还有不可写入的指针里面写东西,bug。主打一个叛逆
-
10.26 修好上面的bug,但是屎山
-
11.2 终于学到了结构体,然后就会用结构体编写屎山了,不过好歹可以运行了 。喜欢用malloc,但是人菜瘾大老是报错
-
11.11 学习了typedef、链表结构,尝试使用来改造屎山
-
11.13 好像项目有点小大,bug还没改完,但是先改成项目类型分开功能
-
11.14 修改了大部分bug,但是保留了一小部分bug来表现我最真实的水平,打DOTA去了
- 修完三个小时bug的我belike:
-
11.15 我们安全了,暂时的…….程序终于可以run了
-
-
AI对上面代码的评价是
AI
您的代码中存在一些问题。我将依次指出并修正它们。
- malloc返回的是指针类型,而您在这里定义了一个Node类型的指针,应该去掉*(double*):
c Node* newNode = (Node*)malloc(sizeof(Node*));
改为:
c Node* newNode = (Node*)malloc(sizeof(Node));
- 您没有正确地使用字符串拷贝函数strcpy()。在这个函数中,第二个参数应该是要复制到的位置(即目标),而不是源字符串。正确的用法是:
c strcpy(newNode->name, name_input);
- 您在链表创建和插入节点的时候有问题。每次添加新节点的时候,都会使head变量失效。您的add_node函数需要传入一个指向头结点的指针,然后在这个函数中修改这个头结点的值:
c void add_node(Node** head_ref, char* name, int price, int number, float repurchase_rate) { Node* new_node = (Node*)malloc(sizeof(struct node)); // ... 其他初始化 new_node->next = (*head_ref); (*head_ref) = new_node; }
这样,您就可以从main函数中调用它了:
c goods_data = add_node(&goods_data, name_input, price, number, repurchase_rate);
- 在delect_node函数中,当删除第一个元素时,需要让头指针指向下一个元素。否则,程序将试图访问已经被释放的内存区域。修改如下:
c if(last_node == NULL) { free(head); head = current->next; return; } last_node->next = current->next; free(current);
- 在print_nodelist函数中,您忘记了为计数器加一:
c printf("%d.", ++cnt);
- 在modify_nodedata函数中,当位置大于链表长度时,会发生段错误。因为当计数器超出范围后,current就不再是一个有效的指针。因此,在调用之前,应先检查链表的长度。例如:
c for (int i = 1; i < length + 1; i++) { if(i == position) break; current = current->next; }
- 最后,seach_node函数似乎存在问题。在while循环中的条件似乎不需要,因为您只需要遍历整个链表即可。您还可以考虑在找到元素之后跳出循环。这里可能需要进一步讨论。此外,当元素不存在时,应当返回NULL。例如:
c Node* seach_node(Node* head, char* name_input) { for (int i = 1; head != NULL; i++) { if (!strcmp(head->name, name_input)) { print_nodedata(head, i); return head; } head = head->next; } printf("该商品不存在\\n"); return NULL; }
希望以上建议能对您有所帮助!如果您还有其他疑问,请随时问我。
-
AI提出的bug修改
- [x] head重复创建
- [x] delect删除head
-
目前功能调试情况
- [x] 添加商品
- [x] 删除商品
- [x] 修改商品
- 查询商品
- [x] 列表查询
- [x] 名称查询
BUG修改记录
-
print_nodedata,对链表外访问
void print_nodedata(Node* head,int position) { Node* current = head; for(int cnt = 1 ;cnt <= position ;cnt++ ){ current = current->next; } }
- 修正:
- 增加获取链表长度
Node* current = head; int len = 0;//获取链表长度 while ( current->next != NULL ){ len++; current = current->next; } current = head;//重置current for(int cnt = 0;cnt < len_Linklist ;cnt++ ){ current = current->next; }
- 修正:
-
储存的字符串输出乱码 11.14 17:09
//问题源 //商品名字 动态分配内存储存name,避免浪费空间 //name_input ---> name(结构体中) char* name = (char*)malloc(strlen(name_input)+1); strcpy(name_input,name); newNode->name = name;
-
printf访问不存在的值
- printf访问不存在的值
void print_nodelist(Node* head) { int cnt = 0; Node* current = head; while(current != NULL){ //商品信息输出 printf("%d.%s\\n",++cnt,current->name); current = current->next; } }
- 当最后一次也就是current指向了NULL的时候,printf会访问NULL输出,bug
-
不安全的边界,可能访问内存外的东西
- position大于节点个数时,会访问内存外,然后程序崩溃(别问我为什么知道)
void delect_node(Node** head,int position) { Node* current = *head; Node* last_node = *head; for(int cnt = 1 ;cnt <= position ;cnt++ ){ last_node = current; if(current->next != NULL ){ //ĩλ¼ì²â current = current->next; } }
- 修正
- 新增一个len_Linklist函数用于计数节点个数
- [x] 改正后函数逻辑改变,后面的内容需要重构
for(int cnt = 0 ;cnt < len_Linklist(*head) ;cnt++ ){ last_node = current; if(current->next != NULL ){ //末位检测 current = current->next; } }
-
商品删除有误
- 选的是apple删除的是banana
- 是由于current = current→next运行过早导致的
-
链表技术总结
-
二级指针
-
在C语言中,函数调用进入的东西都是另外拷贝一份独立操作的
-
也就是说int a变量进去的是他的拷贝a’
-
在我们传入int pa时,拷贝了pa的值(也就是a的地址0X114514*)
-
然后在函数中使用a的地址0X114514 0X114514——>a
-
这样才导致main函数中的变量a变化
-
那么现在我们回来看到传入的指针headRef
void insert(struct Node** headRef, int position, int value)
-
图示headRef的变化过程
-
- 进入函数后我们就只能对拷贝出来的指针(也就是右边的那个)进行操作了
- 比方说,现在我要改变链表的头部,把链表的头部改为下一个指针
- 我把链表的头部改为下一个指针,也就是(0X114514)变成了(0X1919)
- 然后函数运行结束,返回main函数中
- 原来拷贝的指针释放,而原来main函数中的指针不变
-
那么如果我们现在变成用二级指针呢?
- 二级指针,也就是指针的地址
- 比方说,指针int* pa = 0X114514(a的地址)
- 那么pa的指针也就是int* ppa = 0X810(一个指向**-->**储存着0X114514的内存的地址)
insert( &pa, position, value) //取指针pa的地址(0X810)输入进去
- 那么这时候,我们就可以通过改变0X810指向的值(0X114514)来改变外面的指针了
总结一下:
- 如果我们想对一个变量(或者地址)在函数中进行修改
- 那么我们就要传入它的指针
- 因为C语言的函数调用值都是拷贝一份另外在函数中使用的
- WARNING!函数内二级指针记得当地址用!(曾经有个悬崖边上插着个牌子写着warning,然后一个程序员看了就从上面跳了下去)
-
-
关于链表中结构体以变量的形式储存
- Q:为什么在链表中的节点(newNode)是指针(Node)而不是类似int的(Node)?*
- A:如下…
- 类似int的Node类型,在C语言中是只能【静态内存分配】来分配内存的,
- 也就是说在程序一运行时就分配好了内存,程序运行的过程中是不可以修改的,
- 比如说数组arr[x],创建后就通过【静态内存分配】分配好了内存,
- 你不能更改数组的大小,也就是不能更改它的内存大小
- 而作为int的Node指针类型,在C语言中则除了【静态内存分配】,还可以使用【动态内存分配】的方式分配内存(malloc,calloc)
- 也就是说,如果我使用了Node类型而不是Node*,那么我在程序运行过程中,是不可以通过【静态内存分配】来定义新节点的
- 而我用Node*指针类型作为节点,我虽然不能通过【静态内存分配】来分配空间,但是我还可以用malloc来通过【动态内存分配】的方式分配新空间,来创建新节点
- 类似int的Node类型,在C语言中是只能【静态内存分配】来分配内存的,
-
循环的首位判断
- 输入 == 输出
- 可以作为判断首位的依据
Node* current = *head; Node* last_node = *head; for(int cnt = 1 ;cnt <= position ;cnt++ ){ last_node = current; if(current->next != NULL ){ //末位检测 current = current->next; } }
-