使用链接列表对教室线进行建模

时间:2018-01-24 08:19:38

标签: 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;    
}

3 个答案:

答案 0 :(得分:0)

我认为你需要的只是:

void place(LinePtr previousPtr, char array[100]) 
{
    return add (previousPtr, array);
}

// If lPtr == null then this is an insertion at the end of the list
// If lPtr != null then this is an insertion into the list after lPtr.
void add(LinePtr *lPtr, char array[100]) 
{
    LinePtr newPtr = malloc(sizeof(Line));
    strcpy(newPtr->name, array);
    newPtr->nextPtr = NULL;

    LinePtr lastPtr = *lPtr;
    if(*lPtr == NULL) 
    {
       *lPtr = newPtr;
       return;
    }  
    while(lastPtr->nextPtr != NULL) {
        lastPtr = lastPtr->nextPtr;
    }
    lastPtr->nextPtr = newPtr;
}

您的添加功能已经添加或插入。

答案 1 :(得分:0)

place函数必须知道旧节点。您可以提供新名称和旧名称。

//place before the other name
void place(LinePtr *list, char *newName, char *otherName)
{
    if(list == NULL)
    {
        printf("the given previous node cannot be NULL");
        return;
    }

    Line *walk = *list;
    Line *prev = NULL;
    while(walk)
    {
        if(strcmp(walk->name, otherName) == 0)
        {
            //found
            LinePtr newPtr = (LinePtr)malloc(sizeof(Line));
            strcpy(newPtr->name, newName);
            newPtr->nextPtr = walk;
            if(prev)
                prev->nextPtr = newPtr;
            else
                *list = newPtr;
            break;
        }
        prev = walk;
        walk = walk->nextPtr;
    }
}

void place_after_otherName(LinePtr *list, char *newName, char *otherName)
{
    if(list == NULL)
    {
        printf("the given previous node cannot be NULL");
        return;
    }

    Line *walk = *list;
    while(walk)
    {
        if(strcmp(walk->name, otherName) == 0)
        {
            //found
            LinePtr newPtr = (LinePtr)malloc(sizeof(Line));
            strcpy(newPtr->name, newName);
            newPtr->nextPtr = walk->nextPtr;
            walk->nextPtr = newPtr;
            break;
        }
        walk = walk->nextPtr;
    }
}

add函数按预期工作。例如:

int main(void) 
{
    LinePtr startPtr = NULL;
    add(&startPtr, "x");
    add(&startPtr, "y");
    add(&startPtr, "z");

    place(&startPtr, "A", "z");
    place_after_otherName(&startPtr, "B", "z");

    LinePtr p = startPtr;
    while(p)
    {
        printf(p->name);
        p = p->nextPtr;
    }

    printf("\n");
    return 0;
}

答案 2 :(得分:0)

John,您的代码非常尴尬,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)

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

仔细看看,如果您有其他问题,请告诉我。