为什么名为 "traverse" 的函数在我的代码中不起作用?
Why does the function named "traverse" not work on my code?
在下面的例子中,我创建了一个链表,我可以成功添加数字。然而,在
执行结束,名为“遍历”的函数不起作用。我该如何解决这个错误?
这是我的代码:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct node
{
int data;
struct node*prev;
struct node*next;
};
void add( node*head,int number )
{
node*ptr = NULL;
if( head == NULL )
{
head = (node*)malloc(sizeof(node));
head->data = number;
head->next = NULL;
head->prev = NULL;
ptr = head;
}
else
{
ptr->next = (node*)malloc(sizeof(node));
ptr->next->prev = ptr;
ptr = ptr->next;
ptr->data = number;
ptr->next = NULL;
}
}
void traverse( node* head )
{
while( head != NULL )
{
printf("%d ",head->data);
head = head->next;
}
}
int main( void )
{
node *head = NULL;
int number;
char response;
printf("%s\n","Do you want to enter a number in linked list(y/n)?" );
scanf("%c",&response);
while( response == 'y' || response == 'Y' )
{
printf("\nEnter num..> ");
scanf("%d",&number);
add(head,number);
printf("%s\n","Do you want to continue(y/n)?" );
response = getche();
}
printf("\nYour doubly linked list\n");
traverse(head);
getch();
return 0;
}
调用“traverse”时,控制台打印space如下图。
如果您决定使用 C,那么从注释继续,您正在尝试更新 add()
中指针 head
的本地副本。如前所述,您有两个选择,要么将 add()
的 return 类型更改为 node *add()
,这样您就可以 return ptr
并分配为新的 head
返回main()
,或者将head
的地址作为第一个参数传入,更新add()
.
中原指针地址存储的节点
您可以将head
的地址传给add()
,如下:
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
(注意: 因为你把head
的地址作为参数传递,你必须从[=中的指向指针的指针中去掉一层间接18=] 通过取消引用 head
(例如 *head
)以更新原始指针地址处的节点。当使用 [=33 进一步取消引用指针时,您还需要使用 (*head)
=] 由于 C operator precedence -- 所以你在应用 ->
之前得到原始指针地址)
请注意,add()
函数使用方法调用 Forward-Chaining 在 O(1) 时间内将每个节点添加到列表中。这也意味着该列表将以与输入相反的顺序保存数字(最后一个)。您有两个选项可以按顺序插入,(1) 每次迭代到列表的末尾并添加一个新的结束节点(对于大型列表来说效率非常低,不再是 O(1) 时间,或者 (2) 使用另一个 tail
始终指向最后一个节点的指针,以允许在 O(1) 时间内按顺序插入。
然后您可以使用
在 main()
中调用您的 add()
函数
add (&head, number);
测试列表实现时,不要让自己为难。没有理由必须在添加到列表中的每个数字之前输入 'y'
然后输入数字并再次输入 'y'
(这会让我发疯......)。只需通过循环将数字添加到您的列表中,您可以稍后进行输入,例如
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
(注意: conio.h
已被删除,getchar()
用于在 windows 上保持终端打开。因为我在Linux,最终的 getchar()
没有编译为我的可执行文件的一部分)
您的 traverse()
函数将起作用,但要养成使用单独的单独指针迭代列表的习惯。这并不总是必需的,并且在 traverse()
中也不需要,因为您可以使用 head
的本地副本,但始终使用临时指针进行迭代并留下原始 head
地址,如果您需要它以便稍后在您的函数中使用,例如
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
另请注意 delete_list()
功能添加到 free()
为您的列表添加的所有内存。您不会总是在 main()
中声明列表,其中内存在退出时被释放。养成跟踪您分配的内存并在指针超出范围之前释放内存的习惯(否则,您将造成内存泄漏)
完整的程序是:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *prev, *next;
} node;
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
void delete_list (node *head)
{
node *iter = head;
while (iter) {
node *victim = iter;
iter = iter->next;
free (victim);
}
}
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
示例Use/Output
$ ./bin/llmess
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/llmess
==16661== Memcheck, a memory error detector
==16661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16661== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16661== Command: ./bin/llmess
==16661==
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
==16661==
==16661== HEAP SUMMARY:
==16661== in use at exit: 0 bytes in 0 blocks
==16661== total heap usage: 21 allocs, 21 frees, 1,504 bytes allocated
==16661==
==16661== All heap blocks were freed -- no leaks are possible
==16661==
==16661== For counts of detected and suppressed errors, rerun with: -v
==16661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。
在下面的例子中,我创建了一个链表,我可以成功添加数字。然而,在 执行结束,名为“遍历”的函数不起作用。我该如何解决这个错误?
这是我的代码:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct node
{
int data;
struct node*prev;
struct node*next;
};
void add( node*head,int number )
{
node*ptr = NULL;
if( head == NULL )
{
head = (node*)malloc(sizeof(node));
head->data = number;
head->next = NULL;
head->prev = NULL;
ptr = head;
}
else
{
ptr->next = (node*)malloc(sizeof(node));
ptr->next->prev = ptr;
ptr = ptr->next;
ptr->data = number;
ptr->next = NULL;
}
}
void traverse( node* head )
{
while( head != NULL )
{
printf("%d ",head->data);
head = head->next;
}
}
int main( void )
{
node *head = NULL;
int number;
char response;
printf("%s\n","Do you want to enter a number in linked list(y/n)?" );
scanf("%c",&response);
while( response == 'y' || response == 'Y' )
{
printf("\nEnter num..> ");
scanf("%d",&number);
add(head,number);
printf("%s\n","Do you want to continue(y/n)?" );
response = getche();
}
printf("\nYour doubly linked list\n");
traverse(head);
getch();
return 0;
}
调用“traverse”时,控制台打印space如下图。
如果您决定使用 C,那么从注释继续,您正在尝试更新 add()
中指针 head
的本地副本。如前所述,您有两个选择,要么将 add()
的 return 类型更改为 node *add()
,这样您就可以 return ptr
并分配为新的 head
返回main()
,或者将head
的地址作为第一个参数传入,更新add()
.
您可以将head
的地址传给add()
,如下:
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
(注意: 因为你把head
的地址作为参数传递,你必须从[=中的指向指针的指针中去掉一层间接18=] 通过取消引用 head
(例如 *head
)以更新原始指针地址处的节点。当使用 [=33 进一步取消引用指针时,您还需要使用 (*head)
=] 由于 C operator precedence -- 所以你在应用 ->
之前得到原始指针地址)
请注意,add()
函数使用方法调用 Forward-Chaining 在 O(1) 时间内将每个节点添加到列表中。这也意味着该列表将以与输入相反的顺序保存数字(最后一个)。您有两个选项可以按顺序插入,(1) 每次迭代到列表的末尾并添加一个新的结束节点(对于大型列表来说效率非常低,不再是 O(1) 时间,或者 (2) 使用另一个 tail
始终指向最后一个节点的指针,以允许在 O(1) 时间内按顺序插入。
然后您可以使用
在main()
中调用您的 add()
函数
add (&head, number);
测试列表实现时,不要让自己为难。没有理由必须在添加到列表中的每个数字之前输入 'y'
然后输入数字并再次输入 'y'
(这会让我发疯......)。只需通过循环将数字添加到您的列表中,您可以稍后进行输入,例如
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
(注意: conio.h
已被删除,getchar()
用于在 windows 上保持终端打开。因为我在Linux,最终的 getchar()
没有编译为我的可执行文件的一部分)
您的 traverse()
函数将起作用,但要养成使用单独的单独指针迭代列表的习惯。这并不总是必需的,并且在 traverse()
中也不需要,因为您可以使用 head
的本地副本,但始终使用临时指针进行迭代并留下原始 head
地址,如果您需要它以便稍后在您的函数中使用,例如
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
另请注意 delete_list()
功能添加到 free()
为您的列表添加的所有内存。您不会总是在 main()
中声明列表,其中内存在退出时被释放。养成跟踪您分配的内存并在指针超出范围之前释放内存的习惯(否则,您将造成内存泄漏)
完整的程序是:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *prev, *next;
} node;
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
void delete_list (node *head)
{
node *iter = head;
while (iter) {
node *victim = iter;
iter = iter->next;
free (victim);
}
}
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
示例Use/Output
$ ./bin/llmess
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/llmess
==16661== Memcheck, a memory error detector
==16661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16661== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16661== Command: ./bin/llmess
==16661==
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
==16661==
==16661== HEAP SUMMARY:
==16661== in use at exit: 0 bytes in 0 blocks
==16661== total heap usage: 21 allocs, 21 frees, 1,504 bytes allocated
==16661==
==16661== All heap blocks were freed -- no leaks are possible
==16661==
==16661== For counts of detected and suppressed errors, rerun with: -v
==16661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。