0

我在编码方面相当新,并且使用链表和结构获得了一项艰巨的任务。任务是创建一个音乐数据库,以便您可以读取艺术家的专辑和曲目,并将它们存储在节点中。

目前,当我运行程序时,当我从 .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';
}    
4

2 回答 2

1

有一堆类型错误会溢出-Wall(正如@user120115 所建议的那样)。

有几件事立刻跳出来:

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

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

于 2012-04-12T02:41:41.410 回答
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. ...(基本键在“调试”菜单中。)

在运行时,您也可以右键单击变量并选择Watch,即“watch 'discTemp'”。如果不存在,请选择“调试”>“调试窗口”>“手表”。

现在,当您逐步浏览代码时,您将看到变量的值等。

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 中添加命令行参数,您必须使用:

“项目”>“设置程序的参数”

于 2012-04-12T02:41:49.753 回答