[英]Getting input for linked list fgets & scanf C
我創建了這個鏈表,但我無法同時獲取字符數組和整數。 有什么建議? 我一直在論壇和谷歌上上下浮動,但我似乎無法同意任何事情。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int id;
char name[40];
struct node *next;
struct node *prev;
} info;
int insert (int num, char *Name);
void print ();
struct node *root; // Declare root a pointer to the first node
int main () {
int n, Id, i;
char nAme[40];
char *name_ptr;
root = NULL; // List is empty
printf ("How many list entries do you want to make?\n");
scanf (" %d ", &n);
n = 2;
for (i = 0; i < n; i++) {
printf ("Enter name: ");
fgets (nAme, 40, stdin);
printf ("Enter id number: ");
scanf ("%d", &Id);
name_ptr = &nAme[0];
insert (Id, name_ptr);
print ();
}
}
int insert (int num, char *Name)
{
// Get memory
struct node *temp = (struct node *) malloc (sizeof (struct node));
if (temp == NULL) {
printf ("Some malloc problem.\n");
return 1;
}
temp->id = num;
strcpy (temp->name, Name);
temp->next = root;
root = temp;
}
void print ()
{
struct node *temp = root;
while (temp != NULL) {
printf ("%d\n%s\n", temp->id, temp->name);
temp = temp->next;
}
printf ("\n");
}
對不起,我對這個網站有點陌生,所以格式可能會說 javascript,但正如你可能知道的那樣,它是 C。
有許多問題密謀反對你。 第一個,重要的,但不是停止顯示,是fgets
將讀取並包括newline
。 這意味着您的所有nAme
(假設您的意思是name
)條目在末尾都有一個嵌入的換行符。 當您嘗試搜索或比較列表中的特定名稱時,這將導致各種破壞。
一個巨大的問題是,您無法驗證您是否甚至從用戶那里收到要添加到列表中的輸入。 始終,始終驗證用戶輸入。 這意味着至少要檢查您是否確實有要存儲的輸入。 例如
printf ("Number of list entries to make?: ");
if (scanf (" %d%*c", &n) != 1) {
fprintf (stderr, "error: invalid no. of entries.\n");
return 1;
}
for (i = 0; i < n; i++) {
printf ("Enter name: ");
if (!fgets (name, MAXC, stdin)) {
fprintf (stderr, "error: invalid name.\n");
return 1;
}
rmcrlf (name); /* strip trailing newline from name */
printf ("Enter id number: ");
if (scanf ("%d%*c", &id) != 1) {
fprintf (stderr, "error: invalid ID.\n");
return 1;
}
insert (id, name);
}
(注意: rmcrlf
(刪除回車換行符)函數從名稱值中修剪尾隨的換行符或錯誤的回車符)
更實質性的問題是對您正在創建的列表類型(單/雙鏈接,傳統的head/tail ,循環,什么?)的明顯混淆,區別在於傳統的head/tail將使用NULL
值來標記結束/列表的開頭,而循環列表將最后一個節點指向第一個,允許從列表周圍的任何點進行迭代。
您似乎想要一個帶有prev
和next
指針的雙向鏈表,但您絕對沒有嘗試以任何方式處理prev
指針。 目前還不清楚您是想要一個圓形還是頭/尾NULL
列表。 無論你選擇,你必須考慮你的設定值prev
和next
指針每次insert
你怎么做。 與其余插入相比,您是否設置第1st
(和循環列表中的2nd
個)會有所不同。 下面是一個插入函數的例子,它可以正確處理循環列表的添加。 頭/尾NULL
列表不需要對第二個節點進行特定處理。
struct node *insert (int num, char *name)
{
struct node *temp = malloc (sizeof *temp); /* allocate node */
if (!temp) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
temp->id = num;
strcpy (temp->name, name);
if (!root) { /* empty list - insert first node */
temp->next = temp->prev = temp;
root = temp;
}
else if (root == root->next) { /* insert 2nd node */
temp->next = root;
root->prev = temp;
root->next = temp;
temp->prev = root;
}
else { /* insert all remaining nodes at end */
root->prev->next = temp;
temp->prev = root->prev;
temp->next = root;
root->prev = temp;
}
return temp;
}
(注意:函數類型是struct node *
。在這里返回int
值幾乎沒有用。您可以通過在失敗時返回NULL
來判斷成功/失敗,並且您可以將插入的節點用於任何后插入處理也是需要的。,不要投的回報率malloc
,你只需要采取sizeof
對象指向被分配的指針)
不要在代碼中使用幻數。 它們使代碼更難維護。 如果您需要一個常量來設置name
的最大字符數,則使用#define MAXC 40
定義一個或聲明一個全局enum
來定義常量(例如enum { MAXC = 40 };
)。
將這些部分放在一起並修復一些其他問題,您可以編寫雙向鏈接循環列表,如下例所示。 您可以通過簡單地使用傳統的頭/尾NULL
列表來稍微簡化代碼。
仔細查看代碼並了解所做的更改(您根本不需要name_ptr
,並且您沒有設置n=2;
在使用scanf
輸入n
之后)。 還要注意如何處理輸入緩沖區中的'\\n'
字符scanf
(例如stdin
)。 如果您沒有考慮到'\\n'
,您會發現scanf
跳過了您的輸入,因為它將輸入緩沖區中的'\\n'
作為您的輸入。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 40
struct node {
int id;
char name[MAXC];
struct node *next;
struct node *prev;
} info;
struct node *insert (int num, char *name);
void print (void);
void delete_list (void);
void rmcrlf (char *s);
struct node *root; /* Declare root a pointer to the first node */
int main (void) {
int i, id, n;
char name[MAXC];
root = NULL; /* List is empty */
printf ("Number of list entries to make?: ");
if (scanf (" %d%*c", &n) != 1) {
fprintf (stderr, "error: invalid no. of entries.\n");
return 1;
}
for (i = 0; i < n; i++) {
printf ("Enter name: ");
if (!fgets (name, MAXC, stdin)) {
fprintf (stderr, "error: invalid name.\n");
return 1;
}
rmcrlf (name);
printf ("Enter id number: ");
if (scanf ("%d%*c", &id) != 1) {
fprintf (stderr, "error: invalid ID.\n");
return 1;
}
insert (id, name);
}
print();
delete_list();
return 0;
}
struct node *insert (int num, char *name)
{
struct node *temp = malloc (sizeof *temp); /* allocate node */
if (!temp) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
temp->id = num;
strcpy (temp->name, name);
if (!root) { /* empty list - insert first node */
temp->next = temp->prev = temp;
root = temp;
}
else if (root == root->next) { /* insert 2nd node */
temp->next = root;
root->prev = temp;
root->next = temp;
temp->prev = root;
}
else { /* insert all remaining nodes at end */
root->prev->next = temp;
temp->prev = root->prev;
temp->next = root;
root->prev = temp;
}
return temp;
}
void print (void)
{
struct node *temp = root;
printf ("\n Id Name\n ---- ---------------------\n");
while (temp->next != root) {
printf (" %4d %s\n", temp->id, temp->name);
temp = temp->next;
}
printf (" %4d %s\n", temp->id, temp->name);
printf ("\n");
}
/* free memory for all nodes in list */
void delete_list (void)
{
struct node *temp = root->next;
while (temp != root) {
struct node *victim = temp;
temp = temp->next;
free (victim);
}
if (temp) free (temp);
}
/** stip trailing newlines by overwriting with null-terminating char.
* str is modified in place.
*/
void rmcrlf (char *s)
{
if (!s || !*s) return;
for (; *s && *s != '\n'; s++) {}
*s = 0;
}
示例使用/輸出
$ ./bin/ll_double_cir
Number of list entries to make?: 3
Enter name: cats
Enter id number: 3
Enter name: dogs
Enter id number: 17
Enter name: mice
Enter id number: 123
Id Name
---- ---------------------
3 cats
17 dogs
123 mice
內存使用/錯誤檢查
在您編寫的任何動態分配內存的代碼中,您對分配的任何內存塊都有兩個責任:(1)始終保留一個指向內存塊起始地址的指針,(2) 當它不是時可以釋放它不再需要。
您必須使用內存錯誤檢查程序來確保您沒有在分配的內存塊之外/之外寫入,嘗試讀取或基於未初始化的值進行跳轉,並最終確認您已釋放所有內存已分配。 對於 Linux valgrind
是正常的選擇。 用法很簡單,只需通過它運行您的代碼並檢查結果:
$ valgrind ./bin/ll_double_cir
==14595== Memcheck, a memory error detector
==14595== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14595== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14595== Command: ./bin/ll_double_cir
==14595==
Number of list entries to make?: 3
Enter name: cats
Enter id number: 3
Enter name: dogs
Enter id number: 17
Enter name: mice
Enter id number: 123
Id Name
---- ---------------------
3 cats
17 dogs
123 mice
==14595==
==14595== HEAP SUMMARY:
==14595== in use at exit: 0 bytes in 0 blocks
==14595== total heap usage: 3 allocs, 3 frees, 192 bytes allocated
==14595==
==14595== All heap blocks were freed -- no leaks are possible
==14595==
==14595== For counts of detected and suppressed errors, rerun with: -v
==14595== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
始終確認所有堆塊都已釋放——不可能發生泄漏,而且同樣重要錯誤摘要:0 個上下文中的 0 個錯誤。
查看更改,如果您有任何疑問,請提問。
什么是不做?
看代碼:
root 應初始化為 NULL。
插入可以稱為 inert(Id, nName); 插入插入(Id,name_ptr);
在插入中,你應該初始化 ->prev = NULL;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.