链接列表:读取txt文件,节点不保存

时间:2012-04-12 02:26:12

标签: c

我在编码方面比较陌生,并且使用链接列表和结构进行了艰难的任务。作业是创建一个音乐数据库,以便您可以阅读艺术家专辑和曲目,并将它们存储在节点中。

目前,当我运行程序时,当我从.txt文件读取数据时,没有存储任何节点。最终,我开始怀疑我对某些指针的使用是否正确,因为我遇到了分段错误。我做错了什么,是否有可以使其发挥作用的修复工具?

我的代码(包含了所需的库):

结构和宏定义:

#define LINEBUFFERSIZE 256

struct song
{
    char *songName_p;
    int trackNumber;
    struct song *nextSong_p;
};

struct disc
{
    char *discName_p;
    int year;
    struct song *song_p;
    struct disc *nextDisc_p;

};

struct artist
{
    char name[20];
    char *artistName_p;
    struct disc *disc_p;
    struct artist *nextArtist_p;
};
struct artist *end = (struct artist *) NULL;   //NEW
struct artist *startPtr = (struct artist *) NULL; //NEW
struct artist *find(struct artist *, char * );//NEW
//NEW

类型定义:

typedef struct artist artist_t;
typedef struct disc disc_t;
typedef struct song song_t;

typedef struct artist *artistNodePtr;
typedef struct disc *discNodePtr;
typedef struct song *songNodePtr;

功能定义:

void InsertArtist(struct artist *New);
artistNodePtr initializenode(char *name);
artistNodePtr findOrInsertArtist(artistNodePtr *sPtr, char *name);
discNodePtr  findOrInsertDisc(discNodePtr *sPtr, char *discID, int releaseYear);
void  findOrInsertSong(songNodePtr *sPtr, char *songID, int trackID);
void getNextLine(char buffer[], int bufferSize, FILE *fptr);
void printlist( struct artist *ptr );
void printnode(struct artist *ptr);

主要方法:

int main(int argc, char *argv[])
{
    char name[20];
    struct artist *newNodePointer;
    char lineBuffer[LINEBUFFERSIZE];
    artistNodePtr startPtr = NULL; /* initially the artist list is empty */
    FILE *musicFile;
    char *artistTemp, *discTemp, *yearTemp, *trackTemp, *songTemp;
    int year, track, menu = 1;
    artistNodePtr theArtist;
    // discNodePtr theDisc;

    if (argc==1)
    {
        printf(" Must supply a file name as command line argument/n");
        return 0;
    }

    if ((musicFile = fopen(argv[1], "r")) == NULL)
    {
        printf ("Error opening music file.  Program terminated/n");
        return 0;
    }

    getNextLine(lineBuffer, LINEBUFFERSIZE, musicFile);
    while (!feof(musicFile))
    {
        artistTemp = strtok(lineBuffer,";");
        if (artistTemp == NULL)
        {
            printf("Error parsing input file; Program is terminated\n");
            return 0;
        }
        discTemp = strtok(NULL ,";");
        if (discTemp == NULL)
        {
            printf("Error parsing input file; Program is terminated\n");
            return 0;
        }
        yearTemp  = strtok(NULL ,";");
        if (yearTemp == NULL)
        {
            printf("Error parsing input file; Program is terminated\n");
            return 0;
        }
        trackTemp = strtok(NULL ,";");
        if (trackTemp == NULL)
        {
            printf("Error parsing input file; Program is terminated\n");
            return 0;
         }
        songTemp = strtok(NULL ,"\n");
        if (songTemp == NULL)
        {
            printf("Error parsing input file; Program is terminated\n");
            return 0;
        }
        year = atoi(yearTemp);
        track = atoi(trackTemp);
        theArtist = findOrInsertArtist(&startPtr, artistTemp);
  //    theDisc = findOrInsertDisc(&(theArtist->disc_p), discTemp, year);
        //findOrInsertSong(&(theDisc->song_p), songTemp, track);
        getNextLine(lineBuffer, LINEBUFFERSIZE, musicFile);
    }  /* end of while loop */

    while (menu != 0)
    {
        printf("1 to display entire catalog \n");
        printf("2 to display alls songs by a given artist\n");
        printf("3 to display all songs on a given disc\n");
        printf("0 to exit the library\n ");
        scanf("%d", &menu);
        switch (menu){
        case 1: printlist(startPtr);
            break;

        case 2:
            printf("enter an artist name");
            scanf("%s", name );
            newNodePointer = find(startPtr, name );
            if (newNodePointer==NULL)
            {
                newNodePointer = initializenode(name );
                InsertArtist(newNodePointer);
            }

        }
    } /* end main */
}

功能定义:

artistNodePtr findOrInsertArtist(artistNodePtr *sPtr, char *name )
   {

sPtr = initializenode(name);
    InsertArtist(sPtr);

    if(!sPtr)
        sPtr = find(startPtr, name);
    return sPtr;
}
void printlist( struct artist *ptr ){
    while(ptr!= NULL){
    printnode(ptr);
    ptr = ptr->nextArtist_p;
    }
    }
void printnode(struct artist *ptr)
{
    printf("Name %s\n", ptr->name);

}

artistNodePtr initializenode(char *name)
{

    struct artist *newNodePtr = (artistNodePtr)malloc(sizeof(artist_t));
    if (newNodePtr != NULL)
    {
        newNodePtr->artistName_p = (char*)malloc((strlen(name)+1)*sizeof(char));
        if (newNodePtr->artistName_p != NULL)
        {
           strcpy(newNodePtr->artistName_p, name);
            newNodePtr->nextArtist_p = NULL;
            newNodePtr->disc_p = NULL;

            return newNodePtr;
        }   
    }
}

void InsertArtist(struct artist *New)
   {
    //NEW
    struct artist *temp, *prev;

    if(startPtr == NULL)
    {
        startPtr = New;
        end = New;
       startPtr->nextArtist_p = NULL;
        return;
    }
    temp = startPtr;

    while(strcmp(temp->name, New->name) < 0)
    {
        temp = temp->nextArtist_p;
        if(temp == NULL)
            break;
     }
    if(temp == startPtr)
    {
        New->nextArtist_p = startPtr;
        startPtr = New;
    }
    else
    {
       prev = startPtr;
        while(prev->nextArtist_p != temp)
        {
            prev = prev->nextArtist_p;
        }
        prev->nextArtist_p = New;
        New-> nextArtist_p = temp;
            if(end == prev)
            end = New;
    }
}

 struct artist *find(struct artist *newNodePointer, char *name)
{
    //NEW
    while (strcmp(name, newNodePointer->name )!=0)
    {
        newNodePointer = newNodePointer->nextArtist_p;
        if (newNodePointer == NULL)
            break;
    }
    return newNodePointer;
}


void getNextLine(char buffer[], int bufferSize, FILE *fptr)
{
    char temp;
    int i = 0;

    buffer[0] = fgetc(fptr);
    while ( (!feof(fptr)) && (buffer[i] != '\n') &&  i<(bufferSize-1))
    {
        i = i +1;
        buffer[i]=fgetc(fptr);
    }

    if ((i == (bufferSize-1)) && (buffer[i] != '\n'))
    {
        temp = fgetc(fptr);
        while (temp != '\n')
        {
            temp = fgetc(fptr);
        }
     }

    buffer[i] = '\0';
}    

2 个答案:

答案 0 :(得分:1)

有许多类型错误会随-Wall一起泄露(如@ user120115所示)。

立即跳出的一些事情:

  • 不要强制转换malloc(在C中;除非有充分的理由要避免malloc),否则不要在C ++中使用 new
  • while (!feof(stream))形式的任何循环可能都是错误的,因为feof无法预测未来的EOF,它只会告诉您为什么先前的读取尝试失败(例如,为什么getchar返回EOF)。 feof的要点是区分由于文件结束而导致的“正常”读取失败,以及由于磁盘驱动器捕获火灾导致的“异常”(ferror)读取失败等。
  • InsertArtist需要修改一个列表,因此它必须返回新列表,或者指向旧指针列表,但它既不会。 (但各种findOrInsert函数都有!)

我也在@sarnold限制使用typedef,尽管这无疑是一种品味问题。

答案 1 :(得分:1)

作为开始编译,如果您使用的是GCC,

  

gcc -Wall -Wextra -pedantic -o myprog myprog.c

在我的系统上,我得到:

song.c: In function ‘main’:
song.c:75:16: warning: variable ‘theArtist’ set but not used [-Wunused-but-set-variable]
song.c:74:12: warning: variable ‘track’ set but not used [-Wunused-but-set-variable]
song.c:74:6 : warning: variable ‘year’ set but not used [-Wunused-but-set-variable]

song.c: In function ‘findOrInsertArtist’:
song.c:162:7: warning: assignment from incompatible pointer type [enabled by default]
song.c:163:2: warning: passing argument 1 of ‘InsertArtist’ from incompatible pointer type [enabled by default]
song.c:50:6 : note: expected ‘struct artist *’ but argument is of type ‘struct artist **’
song.c:166:8: warning: assignment from incompatible pointer type [enabled by default]
song.c:167:2: warning: return from incompatible pointer type [enabled by default]

song.c: In function ‘initializenode’:
song.c:197:1: warning: control reaches end of non-void function [-Wreturn-type]

song.c: In function ‘main’:
song.c:158:1: warning: control reaches end of non-void function [-Wreturn-type]

特别留意除了-Wunused之外的所有内容,然后检查是否应该使用那些未使用的变量,并且代码的逻辑有些失败。

然后,当你设法在没有警告的情况下编译时,用valgrind运行程序(如果在windows上我不确定;也许你可以在这里找到一些有用的东西:is-there-a-good-valgrind-substitute-for-windows


编辑:
我注意到你说你使用Code :: Blocks。我建议使用像Vim等编辑器+在命令行上编译。特别是开始。

即便如此;如上面评论中所述,您可以通过转到:

来修改警告级别
"Settings" > "Compiler and debugger ..." > [Compiler Flags]=>[Warnings]

要获得调试,请将-ggdb添加到[Other options]。如果您只想在项目基础上更改此项 - 您可以在以下位置找到相同的选项:

"Project" > "Build options ..."

使用调试符号编译后,可以在调试模式下运行程序。

简单程序:

  1. 右键单击代码中要停止执行的位置。选择“切换断点”。

  2. F8 以调试模式启动程序。

  3. 然后按 F7 逐行执行。

  4. ...(基本键位于“调试”菜单中。)

  5. 在运行时,您还可以右键单击变量并选择 Watch ,即“watch'discTemp'”。并且,如果不存在,请选择“Debug”&gt; “调试窗口”&gt; “手表”。

    现在,当您通过代码时,您将看到变量的值等。

    gdb也可以在命令行上使用。 $ gdb -args ./my_prog arg arg

    如前所述,Valgrind也是一个非常有用的工具。您将获得有关流程中不良行为的信息。即使代码编译没有错误或警告,也不是说程序是理智的。

    Valgrind也在Code :: Blocks中实现,或许更好的说法是:

    $ valgrind ./my_prog arg arg ...
    

    最后一点:一直编译。写几行。编译。进行一次更改编译。 ...


    这些是关于如何使编码更少痛苦的一些提示。希望它有所帮助。


    除了其他人在这里提到的笔记之外,我还想补充一下:

    • menu循环中, 必须 检查scanf()是否返回1,(1成功读取元素,“ %d“在你的情况下) - 如果没有,空缓冲区。因为它现在只是一个整数,所以会导致无限循环。

    • getNextLine()中,您在结尾处有一个while()循环,表示get character from file while character is not newline.如果文件未以换行符结尾,则这是另一个无限循环。另请阅读@sarnold和@torek关于此功能的评论。


    Ps:要在Code :: Blocks中添加命令行参数,您必须使用:

      

    “项目”&gt; “设置程序的参数”