在 struct Node 类型中,有一个char *数据。并且每个节点使用链接列表进行组合。
如果“数据”的类型是INT,则可以。 (例如年龄:23,45,33 ......)但是当类型变成“char *”时,ex。保存名称:“杰克”,“杰伊”,“詹姆斯”。价值是一样的,后者将涵盖前端。例如: 第一次输入:杰克。列表是杰克 第二次输入:周杰伦。列表是 Jay Jay 第三次输入:Jame。该列表是 Jame Jame Jame
代码如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct listNode{
char *data;
struct listNode *next;
} *ListNodePtr;
typedef struct list {
ListNodePtr head;
} List;
List new_list(){
List temp;
temp.head = NULL;
return temp;
}
//Student Courses
void insert_at_front(ListNodePtr* head, char *data){
ListNodePtr new_node = malloc(sizeof(struct listNode));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
void print_list(List *self)
{
ListNodePtr current = self->head;
while(current!=NULL)
{
printf("%s \n", current->data);
current = current->next;
}
printf("\n");
}
int main()
{
char i = 'y';
char *name;
List mylist = new_list();
while(i=='y'){
printf("your name is :");
scanf("%s",name);
insert_at_front(&mylist.head,name);
print_list(&mylist);
}
return 0;
}
答案 0 :(得分:2)
好消息是你对列表操作的想法并不太远......坏消息是它也不是正确的。您拥有的最大绊脚石只是处理用户输入的基础知识,并确保您为存储的每一位数据存储。
在main()
声明char *name;
时,name
是未初始化的指针。它没有指向任何有效的内存,您可以在其中存储组成name
的字符(以及终止 nul-character )。您可以存储在指针中的唯一内容是内存地址 - 并且,有用的是,内存地址必须是有效内存块的开头,其大小足以保存您尝试存储的内容。
在您的代码中,由于name
未指向能够在name
中存储字符的任何有效内存块,因此您立即使用{{1}调用未定义行为 (繁荣! - &#34; Game Over&#34;适用于您的计划)。
由于您正在阅读一个名称,但很少超过64个字符,只需使用固定大小的缓冲区来保留scanf("%s",name);
即可将其传递给name
。不要吝啬缓冲区大小,所以为了确保您可以使用insert_at_front
个字节之类的合理内容。不要在代码中使用幻数,所以如果你需要一个常量:
512
现在对于你的结构,你正在使用一个&#34;包装器&#34; 结构,其中包含基本上包装你的列表的#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
...
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
...
。尽管不是必需的,但这样做很好。但是,由于您使用的是自动存储(并将其作为参数传递)或为其动态分配。由于您使用head
功能,如果您使用自动存储,则必须在new_list
和 <中将mylist
声明为List
类型em>将其作为参数传递给main()
进行初始化。
为什么呢?您无法从new_list()
返回temp
,因为new_list
已在temp
(new_list
的本地)和new_list返回时,new_list
的函数堆栈(包含temp
变量的内存)被销毁(释放以供重用)。
如果你想从new_list
返回指针,那么你必须(1)将先前声明的new_list
从temp
传递给main()
- 或 - (2)动态分配new_list
temp
new_list
分配的存储时间,以便包含temp
的内存存活到temp
在该内存上调用,或者程序结束。正常选项是(2),但如果您充分考虑变量的存储持续时间,则(1)没有任何问题。
进行一些调整,并避免键入一个指针,因为It is NOT a good idea to typedef pointers?,并添加了一个方便的示例,你必须在你的包装器结构中跟踪列表统计信息,你可以做类似于:
free()
为了帮助您在学习过程中使列表更具逻辑性,通常有助于创建一个单独的函数,该函数负责创建添加到列表中的每个节点。这使您可以专注于节点结构中哪些项目需要分配存储空间,并提供一个方便的位置来处理分配(以及验证该分配),以及在一个地方初始化所有值 - 而不会使列表逻辑混乱。您可以实现类似于:
的create_node函数typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
(注意:您已为(1)列表,(2)节点和(3)数据分配)
这使得/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
的逻辑清晰可读。此外,您需要始终对任何列表操作使用正确的insert_at_front
,尤其是在涉及任何分配的情况下,这样可以衡量列表操作的成功/失败。通常会在失败时返回指向添加的节点(此处为新type
)或head
的指针,例如..
NULL
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有2个职责:(1) 始终保留指向内存块的起始地址,(2),当不再需要时,可以释放。现在养成习惯,自己清理一下 - 当你的程序变得更复杂时,它会带来很大的收益。如果您正在处理列表和节点,则编写一个函数以在完成后释放所有数据,节点和列表。只需要一些简单的东西,例如。
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
通过用户输入,您负责验证您获得了良好的输入并且它满足您对输入的任何条件。您还负责确保输入缓冲区的状态为下一个输入操作做好准备。这意味着清除可能留在输入缓冲区中的任何无关字符(例如/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
),这将导致您下次尝试输入失败。一个简单的帮助函数来清空stdin可以帮助你摆脱困境。
stdin
此外,您将使用的每个输入功能都有返回。您必须验证您使用的任何输入函数的返回,以确定是否(1)读取了有效输入,(2)用户是否通过生成手册/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
取消了输入,以及(3) )使用EOF
时是否发生匹配或输入失败。所以经常检查退货!例如:
scanf
在一个简短的例子中,你可以做如下的事情:
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
...
(注意:提示继续允许用户只需按 Enter 表示他想继续输入姓名,任何其他字符都会退出。这就是为什么{ {1}}在提示中显示为默认值 - 您能解释为什么以及如何工作?)
示例使用/输出
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
/* print_list - tweaked for formatted output */
void print_list (list *self)
{
lnode *current = self->head;
while (current != NULL)
{
if (current == self->head)
printf (" %s", current->data);
else
printf (", %s", current->data);
current = current->next;
}
putchar ('\n');
}
/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
list *mylist = new_list();
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
insert_at_front (mylist, name); /* insert name */
fputs ("continue (y)/n: ", stdout); /* prompt to continue */
scanf ("%c", &i); /* read answer (or '\n' from pressing Enter) */
}
printf ("\nfinal list (%zu nodes):", mylist->size);
print_list (mylist);
free_list (mylist); /* don't forget to free memory you allocate */
return 0;
}
内存使用/错误检查
必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了所有已分配的内存。
对于Linux (y)
是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。
$ ./bin/llinshead
enter name: Mickey Mouse
continue (y)/n:
enter name: Donald Duck
continue (y)/n:
enter name: Pluto (the dog)
continue (y)/n:
enter name: Minnie Mouse
continue (y)/n: n
final list (4 nodes): Minnie Mouse, Pluto (the dog), Donald Duck, Mickey Mouse
始终确认已释放已分配的所有内存并且没有内存错误。
仔细看看,如果您有任何其他问题,请告诉我。