簡體   English   中英

獲取鏈表 fgets 和 scanf C 的輸入

[英]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值來標記結束/列表的開頭,而循環列表將最后一個節點指向第一個,允許從列表周圍的任何點進行迭代。

您似乎想要一個帶有prevnext指針的雙向鏈表,但您絕對沒有嘗試以任何方式處理prev指針。 目前還不清楚您是想要一個圓形還是頭/尾NULL列表。 無論你選擇,你必須考慮你的設定值prevnext指針每次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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM