我在编码方面比较陌生,并且使用链接列表和结构进行了艰难的任务。作业是创建一个音乐数据库,以便您可以阅读艺术家专辑和曲目,并将它们存储在节点中。
目前,当我运行程序时,当我从.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';
}
答案 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 ..."
使用调试符号编译后,可以在调试模式下运行程序。
简单程序:
右键单击代码中要停止执行的位置。选择“切换断点”。
F8 以调试模式启动程序。
然后按 F7 逐行执行。
...(基本键位于“调试”菜单中。)
在运行时,您还可以右键单击变量并选择 Watch ,即“watch'discTemp'”。并且,如果不存在,请选择“Debug”&gt; “调试窗口”&gt; “手表”。
现在,当您通过代码时,您将看到变量的值等。
gdb
也可以在命令行上使用。 $ gdb -args ./my_prog arg arg
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; “设置程序的参数”