c - 使用链接列表建模教室线

标签 c data-structures linked-list

我想用我的程序来模拟一条简单的教室线。


函数first:将学生x放在行的前面。
函数out:从行中删除学生x。
功能backToClassroom:在行中打印学生。
函数reverse:逆转行中的学生顺序。
函数place:学生x在学生y后面排位,如果学生y想加入队伍,他/她应该在x后面。
函数add:在行末添加学生x,除非学生为自己代位。


我的问题是:

 我不知道应该如何正确编码addplace,正如我在顶部解释的那样。



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct line {
    char name[100];
    struct line *nextPtr;
};

typedef struct line Line;
typedef Line *LinePtr;

void add(LinePtr *lPtr, char array[100]);
void out(LinePtr *lPtr, char array[100]);
int isEmpty(LinePtr lPtr);
void backToClassroom(LinePtr currentPtr);
void first(LinePtr* lPtr, char array[100]);
void place(LinePtr previousPtr, char array[100]);
static void reverse(LinePtr* lPtr);

int main(int argc, char *argv[]) {

    char order[25];
    LinePtr startPtr = NULL;
    char tempName1[100];
    char tempName2[100];

    gets(order);

    while(strcmp(order, "back to classroom") != 0) {

        if(strcmp(order, "add") == 0) {
            scanf("%s", tempName1);
            add(&startPtr, tempName1);
        }

        if(strcmp(order, "out") == 0) {
            scanf("%s", tempName1);
            out(&startPtr, tempName1);
        }

        if(strcmp(order, "first") == 0) {
            scanf("%s", tempName1);
            first(&startPtr, tempName1);
        }

        if(strcmp(order, "place") == 0) {
            scanf("%s", tempName1);
            scanf("%s", tempName2);
            place(startPtr, tempName2);
        }

        if(strcmp(order, "reverse") == 0) {
            reverse(&startPtr);
        }

        gets(order);
    }

    if(strcmp(order, "back to classroom") == 0) {
        backToClassroom(startPtr);
    }

    return 0;
}


int isEmpty(LinePtr lPtr) {
    return (lPtr == NULL);
}

void backToClassroom(LinePtr currentPtr) {
    if(isEmpty(currentPtr)) {
        printf("line is empty.\n");
    }
    else {
        while(currentPtr != NULL) {
            printf("%s\n", currentPtr->name);
            currentPtr = currentPtr->nextPtr;
        }
    }
}

static void reverse(LinePtr* lPtr) {
    LinePtr previousPtr = NULL;
    LinePtr currentPtr = *lPtr;
    LinePtr afterPtr;
    while(currentPtr != NULL) {
        afterPtr  = currentPtr->nextPtr;  
        currentPtr->nextPtr = previousPtr;   
        previousPtr = currentPtr;
        currentPtr = afterPtr;
    }
    *lPtr = previousPtr;
}

void out(LinePtr *lPtr, char array[100]) {
    LinePtr tempPtr;
    if(strcmp(array, (*lPtr)->name) == 0) {
        tempPtr = *lPtr;
        *lPtr = (*lPtr)->nextPtr;
        free(tempPtr);
    }
    else {
        LinePtr previousPtr = *lPtr;
        LinePtr currentPtr = (*lPtr)->nextPtr;
        while((currentPtr != NULL) && ((strcmp(currentPtr->name, array)) != 0)) {
            previousPtr = currentPtr;
            currentPtr = currentPtr->nextPtr;
        }
        if(currentPtr != NULL) {
            tempPtr = currentPtr;
            previousPtr->nextPtr = currentPtr->nextPtr;
            free(tempPtr);
        }
    }
}

void first(LinePtr* lPtr, char array[100]) {
    LinePtr newPtr = malloc(sizeof(Line));
    strcpy(newPtr->name, array);
    newPtr->nextPtr = (*lPtr);
    (*lPtr) = newPtr;
}

void place(LinePtr previousPtr, char array[100]) {
    if(previousPtr == NULL) { 
       printf("the given previous node cannot be NULL");
       return;      
    }  
    LinePtr newPtr = (LinePtr) malloc(sizeof(Line));
    strcpy(newPtr->name, array);
    newPtr->nextPtr = previousPtr->nextPtr; 
    previousPtr->nextPtr = newPtr;
}

void add(LinePtr *lPtr, char array[100]) {
    LinePtr newPtr = malloc(sizeof(Line));
    LinePtr lastPtr = *lPtr;
    strcpy(newPtr->name, array);
    newPtr->nextPtr = NULL;
    if(*lPtr == NULL) {
       *lPtr = newPtr;
       return;
    }  
    while(lastPtr->nextPtr != NULL) {
        lastPtr = lastPtr->nextPtr;
    }
    lastPtr->nextPtr = newPtr;    
}

最佳答案

约翰,您的代码很尴尬,placeout命令无法按预期工作,并且outback to classroom free您分配的任何内存均无效。立即养成跟踪程序所做的每个分配的习惯,然后在程序结束前分别跟踪free。是的,程序退出时会释放内存,但是当您开始编写用于分配内存的函数(如此处所示)时,必须free该内存,否则随着代码的增长,内存管理将很快变得难以管理。 (因此,请帮自己一个忙,从一开始就学会做吧)

接下来,在您希望用户输入输入的任何界面中,提示用户输入。否则,用户只能看着屏幕上闪烁的光标,怀疑程序是否卡住了??

考虑一下您需要什么数据,并在逻辑上向用户提供提示,将帮助您以不太尴尬的方式布置代码。

Do not typedef pointers。 (例如typedef Line *LinePtr;),这只会使您对指针间接定向的实际级别感到困惑,并且也使其他人无法阅读您的代码。 (当您的代码分布在10个不同的头文件和源文件之间时,这将成为一个更大的问题)尤其是在学习C时,不要typedef指针

让我们转到您的主程序循环。首先,由于您不使用int argc, char *argv[],因此main的正确调用是int main(void),从而明确表明没有参数。应用上面讨论的提示,样式从我的评论更改为重新排序,从而变得更有意义,您的主体可能看起来像:

#define ORDC  25    /* if you need constants, define them or use an enum */
#define NAMC 100    /*      (don't use magic numbers in your code!)     */
...
int main (void) {

    char order[ORDC] = "",  /* initialize all strings to zeros */
        name1[NAMC] = "",
        name2[NAMC] = "";
    line *startptr = NULL;

    /* provide an initial prompt showing valid orders to place */
    printf ("orders [add, out, first, place, reverse, back to classroom]\n");

    for (;;) {  /* loop until done or user cancels input */

        printf ("\norder: ");               /* prompt for order: each iteration */
        if (!fgets (order, sizeof order, stdin)) {  /* use fgets for user input */
            fprintf (stderr, "note: user canceled input.\n");
            return 1;
        }
        rmcrlf (order);        /* trim the trailing '\n' (returning the length) */

        if (strcmp (order, "back to classroom") == 0) {         /* are we done? */
            backtoclassroom (startptr);
            break;
        }

        if (strcmp (order, "reverse") == 0) {          /* reverse takes no name */
            reverse (&startptr);
            continue;
        }

        printf ("name : ");        /* every other function takes a student name */
        if (!fgets (name1, sizeof name1, stdin)) {
            fprintf (stderr, "note: user canceled input.\n");
            return 1;
        }
        rmcrlf (name1);

        if (strcmp (order, "add") == 0)          /* use if, else if, else logic */
            add (&startptr, name1);
        else if (strcmp (order, "out") == 0)
            out (&startptr, name1);
        else if (strcmp (order, "first") == 0)
            first (&startptr, name1);
        else if (strcmp (order, "place") == 0) {      /* place takes two names */
            printf ("after: ");
            if (!fgets (name2, sizeof name2, stdin)) {       /* get name2 here */
                fprintf (stderr, "note: user canceled input.\n");
                return 1;
            }
            rmcrlf (name2);
            place (startptr, name1, name2);
        }
        else    /* handle the Bad Input case */
            fprintf (stderr, "error: invalid order, try again.\n");
    }

    return 0;
}


当您将数组作为参数传递给函数时,间接访问的第一层(表示数组名称后的第一个[..])将转换为指针(实际上是对数组的任何访问,而不是使用sizeof_Alignof或一元&运算符,或者是用于初始化数组的字符串文字,将进行此转换,请参见:C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators (p3))。因此,您的函数仅需要将指向数组的指针作为参数,例如

void first (line **lptr, char *name);
void place (line *lptr, char *name1, char *name2);


(并为变量使用描述性名称,name代替array更有意义)

对于您的out函数,您只有两种情况要处理,(1)我要删除第一个节点吗? (如果是这样,列表地址将成为第二个节点的地址),或者(2)进行迭代,直到找到要删除的节点,然后单击prev->nextptr = current->nextptr; free (current);。您可以执行以下操作:

void out (line **lptr, char *name) 
{
    line *iter = *lptr,
        *prev  = *lptr;

    if (strcmp ((*lptr)->name, name) == 0) {    /* name is first node */
        iter = iter->nextptr;                   /* save pointer to next */
        free (*lptr);                           /* free first */
        *lptr = iter;                           /* set first = saved */
        return;
    }

    while (iter && strcmp (iter->name, name)) { /* find node with name */
        prev = iter;                            /* save previousptr */
        iter = iter->nextptr;
    }
    if (!iter) {    /* handle name not found */
        fprintf (stderr, "error: %s not in list - can't remove.\n", name);
        return;
    }

    prev->nextptr = iter->nextptr;      /* previousptr = nextptr */
    free (iter);                        /* free current */
}


对于您的place功能,您需要2个名字,(1)name1是新学生的名字,以及(2)name2是放置他们的人的名字。将所需的参数添加到place,您可以执行以下操作:

void place (line *lptr, char *name1, char *name2) 
{
    line *iter = lptr;
    line *newptr = malloc (sizeof *newptr);
    strcpy (newptr->name, name1);
    newptr->nextptr = NULL;

    while (iter && strcmp (iter->name, name2))  /* locate after: name */
        iter = iter->nextptr;

    if (!iter) {    /* handle name2 not found */
        fprintf (stderr, "error: %s not in list - can't place %s.\n", 
                name2, name1);
        return;
    }

    newptr->nextptr = iter->nextptr;
    iter->nextptr = newptr;
}


那解决了您两个紧迫的问题。您可能需要调整一两个极端情况,但是将所有部分放在一起(并在函数名称和(之间添加空格,以便较老的眼睛可以更好地阅读您的代码,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ORDC  25    /* if you need constants, define them or use an enum */
#define NAMC 100    /*      (don't use magic numbers in your code!)     */

typedef struct line {
    char name[NAMC];
    struct line *nextptr;
} line;

size_t rmcrlf (char *s);

void add (line **lptr, char *name);
void out (line **lptr, char *name);
int isempty (line *lptr);
void backtoclassroom (line *currentptr);
void first (line **lptr, char *name);
void place (line *lptr, char *name1, char *name2);
static void reverse (line **lptr);

int main (void) {

    char order[ORDC] = "",  /* initialize all strings to zeros */
        name1[NAMC] = "",
        name2[NAMC] = "";
    line *startptr = NULL;

    /* provide an initial prompt showing valid orders to place */
    printf ("orders [add, out, first, place, reverse, back to classroom]\n");

    for (;;) {  /* loop until done or user cancels input */

        printf ("\norder: ");               /* prompt for order: each iteration */
        if (!fgets (order, sizeof order, stdin)) {  /* use fgets for user input */
            fprintf (stderr, "note: user canceled input.\n");
            return 1;
        }
        rmcrlf (order);        /* trim the trailing '\n' (returning the length) */

        if (strcmp (order, "back to classroom") == 0) {         /* are we done? */
            backtoclassroom (startptr);
            break;
        }

        if (strcmp (order, "reverse") == 0) {          /* reverse takes no name */
            reverse (&startptr);
            continue;
        }

        printf ("name : ");        /* every other function takes a student name */
        if (!fgets (name1, sizeof name1, stdin)) {
            fprintf (stderr, "note: user canceled input.\n");
            return 1;
        }
        rmcrlf (name1);

        if (strcmp (order, "add") == 0)          /* use if, else if, else logic */
            add (&startptr, name1);
        else if (strcmp (order, "out") == 0)
            out (&startptr, name1);
        else if (strcmp (order, "first") == 0)
            first (&startptr, name1);
        else if (strcmp (order, "place") == 0) {      /* place takes two names */
            printf ("after: ");
            if (!fgets (name2, sizeof name2, stdin)) {       /* get name2 here */
                fprintf (stderr, "note: user canceled input.\n");
                return 1;
            }
            rmcrlf (name2);
            place (startptr, name1, name2);
        }
        else    /* handle the Bad Input case */
            fprintf (stderr, "error: invalid order, try again.\n");
    }

    return 0;
}

/** remove newline or carriage-return from 's'.
 *  returns new length. 's' must not be NULL.
 */
size_t rmcrlf (char *s)
{
    char *p = s;

    if (!*s)        /* s is empty-string */
        return 0;

    /* find eol or nul-terminating char */
    for (; *p && *p != '\n' && *p != '\r'; p++) {}

    if (*p == '\n' || *p == '\r')   /* validate eol & overwrite */
        *p = 0;
    else                            /* warn - no end-of-line */
        fprintf (stderr, "rmcrlf() warning: no eol detected.\n");

    return (size_t)(p - s);
}

int isempty (line *lptr) 
{
    return (lptr == NULL);
}

void backtoclassroom (line *currentptr) 
{
    printf ("\nline returning to classroom:\n\n");
    if (isempty (currentptr)) {
        printf ("line is empty.\n");
    }
    else {
        while (currentptr != NULL) {
            line *victim = currentptr;          /* ptr to node to free */
            printf ("  %s\n", currentptr->name);
            currentptr = currentptr->nextptr;
            free (victim);                      /* free your memory! */
        }
    }
}

static void reverse (line **lptr) 
{
    line *previousptr = NULL,
        *currentptr = *lptr,
        *afterptr;

    while (currentptr != NULL) {
        afterptr  = currentptr->nextptr;  
        currentptr->nextptr = previousptr;   
        previousptr = currentptr;
        currentptr = afterptr;
    }
    *lptr = previousptr;
}

void out (line **lptr, char *name) 
{
    line *iter = *lptr,
        *prev  = *lptr;

    if (strcmp ((*lptr)->name, name) == 0) {    /* name is first node */
        iter = iter->nextptr;                   /* save pointer to next */
        free (*lptr);                           /* free first */
        *lptr = iter;                           /* set first = saved */
        return;
    }

    while (iter && strcmp (iter->name, name)) { /* find node with name */
        prev = iter;                            /* save previousptr */
        iter = iter->nextptr;
    }
    if (!iter) {    /* handle name not found */
        fprintf (stderr, "error: %s not in list - can't remove.\n", name);
        return;
    }

    prev->nextptr = iter->nextptr;      /* previousptr = nextptr */
    free (iter);                        /* free current */
}

void first (line **lptr, char *name) 
{
    line *newptr = malloc (sizeof *newptr);     /* set size on current *ptr */
    strcpy (newptr->name, name);
    newptr->nextptr = *lptr;
    *lptr = newptr;
}

void place (line *lptr, char *name1, char *name2) 
{
    line *iter = lptr;
    line *newptr = malloc (sizeof *newptr);
    strcpy (newptr->name, name1);
    newptr->nextptr = NULL;

    while (iter && strcmp (iter->name, name2))  /* locate after: name */
        iter = iter->nextptr;

    if (!iter) {    /* handle name2 not found */
        fprintf (stderr, "error: %s not in list - can't place %s.\n", 
                name2, name1);
        return;
    }

    newptr->nextptr = iter->nextptr;
    iter->nextptr = newptr;
}

void add (line **lptr, char *name) 
{
    line *newptr = malloc (sizeof *newptr);     /* set size on current *ptr */
    line *lastptr = *lptr;
    strcpy (newptr->name, name);
    newptr->nextptr = NULL;
    if (*lptr == NULL) {
        *lptr = newptr;
        return;
    }  
    while (lastptr->nextptr != NULL) {
        lastptr = lastptr->nextptr;
    }
    lastptr->nextptr = newptr;    
}


使用/输出示例

$ ./bin/classline
orders [add, out, first, place, reverse, back to classroom]

order: add
name : john doe

order: add
name : mary smith

order: first
name : nancy first

order: place
name : sally second
after: nancy first

order: out
name : john doe

order: back to classroom

line returning to classroom:

  nancy first
  sally second
  mary smith


内存使用/错误检查

在您编写的任何可以动态分配内存的代码中,对于任何分配的内存块,您都有2个责任:(1)始终保留指向该内存块起始地址的指针,因此,(2)在不分配该内存块时可以将其释放需要更长的时间。

必须使用一个内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后确认您可以释放已分配的所有内存。

对于Linux,valgrind是通常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ valgrind ./bin/classline
==22574== Memcheck, a memory error detector
==22574== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==22574== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==22574== Command: ./bin/classline
==22574==
orders [add, out, first, place, reverse, back to classroom]
<snip>
line returning to classroom:

  nancy first
  sally second
  mary smith
==22574==
==22574== HEAP SUMMARY:
==22574==     in use at exit: 0 bytes in 0 blocks
==22574==   total heap usage: 4 allocs, 4 frees, 448 bytes allocated
==22574==
==22574== All heap blocks were freed -- no leaks are possible
==22574==
==22574== For counts of detected and suppressed errors, rerun with: -v
==22574== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


始终确认已释放已分配的所有内存,并且没有内存错误。

仔细检查一下,如果您还有其他问题,请告诉我。

关于c - 使用链接列表建模教室线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48417741/

相关文章:

mysql - 如何按照我的意愿将文本文件最好地上传到数据库中? (在C中)

c++ - 为可维护性和封装性构建 C++ 类层次结构

c++ - 将 C++ 头文件转换为 Python

python - 如何在 C 中创建链表结构

c - 从单链表中删除节点

c - c : Function which deletes every second element of linked list 中的指针

c++ - 我如何查看 Linux .so 或 .a 对象并查看它们包含哪些函数?

c - 如何使用 printf 打印字符串而不打印尾随换行符

C、从txt文件读入struct

c++ - 用于存储元组键的数据结构 : list of parameters relationship