我是新来的,首先,如果我犯错误,我想道歉。我的问题是:我想在C中实现一个B树,使用一个文件来存储树...我的程序从一个Text文件中读取10000个10个字符的字符串,并将该内容存储在.DAT二进制文件中,通过B树组织;然后用户可以搜索字符串。
我正在使用“Cormen,et al - Introduction to Algorithms(3ed)”的算法,这似乎是正确的,清晰的和功能性的。但是,我的程序只是得到运行时错误......比如Segmentation Fault和Infinite Loop。我一直试图调试5天,但没有成功! B树函数是递归的,我个人讨厌...我认为递归会使调试变得如此困难!
我的代码相对较大,它分为2个文件,一个源代码,一个标题。我将在这里发布B树的函数和变量声明。但是,如果有人想要查看完整代码,我会发布一个“iFile.it”链接(是否允许?如果没有,请抱歉!)...
非常感谢您的关注和帮助...对不起这个大问题!
来源链接[不太重要,只有main()]:http://ifile.it/n73drmc/b-tree_file.c
标题链接[所有功能都在这里]:http://ifile.it/u1fa3kp/b-tree_file.h
带字符串的文本文件:http://ifile.it/7hu95ot/arq_5.txt
关于代码的注释:
i)free()有非常奇怪的行为......所以我对它们进行了评论,因为如果我使用它们,我的程序会出错甚至更多!
ii)所有代码评论都是我试图解决问题的想法,必须考虑到我已经尝试过了。
iii)我是一名土生土长的葡语人士,因此功能和变量对于母语为英语的人来说可能有一些奇怪的名字......对不起!
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//defs of pre-compiler
#ifdef __linux
#define PAUSA "read p"
#define CLRSCR "clear"
#else
#define PAUSA "Pause"
#define CLRSCR "cls"
#endif
#define T 5 // minimum degree of B-tree
#define Min (T-1)
#define Max ((2*T)-1)
//
//global vars
FILE *arqt, *arqb;
char VAL[11];
long int lt = 0;
typedef struct {
unsigned int num;
long int pos;
char chave[(Max+1)][11]; //chave in portuguese = key in english!
short int folha; //folha in portuguese = leaf in english!
long int c[(Max+2)];
} Nod;
Nod *raiz = NULL; //raiz in portuguese = root in english!
//
void b_split(Nod *x, unsigned int i, Nod *y) { //B-TREE-SPLIT-CHILD //that function split a B-tree node
Nod *z = NULL;
unsigned int j=1;
z = (Nod*)realloc(z,sizeof(Nod));
fseek(arqb,0,SEEK_END);
z->pos = ftell(arqb);
z->folha = y->folha;
z->num = Min;
for(j=1;j<=Min;j++) {strcpy(z->chave[j],y->chave[j+T]);}
if (y->folha == 0) {
for(j=1;j<=T;j++) {z->c[j] = y->c[j+T];}
}
y->num = Min;
for(j=(x->num + 1);j<=(i+1);j--) {x->c[(j+1)] = x->c[j];}
x->c[(i+1)] = z->pos;
for(j=x->num;j<=i;j--) { strcpy(x->chave[j+1],x->chave[j]); }
strcpy(x->chave[i],y->chave[T]);
x->num = x->num + 1;
fseek(arqb,x->pos,SEEK_SET);
fwrite(x,sizeof(Nod),1,arqb);
fseek(arqb,y->pos,SEEK_SET);
fwrite(y,sizeof(Nod),1,arqb);
fseek(arqb,z->pos,SEEK_SET);
fwrite(z,sizeof(Nod),1,arqb);
//free(z);
//free(y);
}
void b_ins(Nod *x, char *val) { //B-TREE-INSERT-NONFULL //insert a key in nonfull node
unsigned int i=0;
Nod *C = NULL;
i = x->num;
if (x->folha == 1) {
while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {
strcpy(x->chave[(i+1)],x->chave[i]);
i--;
}
strcpy(x->chave[(i+1)],val);
x->num = x->num + 1;
fseek(arqb,x->pos,SEEK_SET);
fwrite(x,sizeof(Nod),1,arqb);
}
else {
while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {i--;}
i++;
C = (Nod*)realloc(C,sizeof(Nod));
fseek(arqb,x->c[i],SEEK_SET);
fread(C,sizeof(Nod),1,arqb);
if (C->num == Max) {
b_split(x,i,C);
if ( strcmp(val,x->chave[i]) > 0 ) {i++;}
}
fseek(arqb,x->c[i],SEEK_SET);
fread(C,sizeof(Nod),1,arqb);
b_ins(C,val);
//free(C);
}
}
void insere_b(char *val) { //B-TREE-INSERT //i believe the problem is here!
Nod *S = NULL,*R = NULL;
R = (Nod*)realloc(R,sizeof(Nod));
R = raiz;
if (R->num == Max) {
S = (Nod*)realloc(S,sizeof(Nod));
/* fseek(arqb,0,SEEK_END);
S->pos = ftell(arqb);
*/
raiz = S;
S->folha = 0;
S->num = 0;
S->c[1] = R->pos;
/* fseek(arqb,S->pos,SEEK_SET);
fwrite(S,sizeof(Nod),1,arqb);
*/
b_split(S,1,R);
b_ins(S,val);
//free(S);
}
else {b_ins(R,val);}
//free(R);
}
void busca_b(Nod *x, char *val) { //B-TREE-SEARCH //self explanatory
unsigned int i=1;
Nod *C = NULL;
while( (i <= x->num) && ( strcmp(val, x->chave[i]) > 0 ) ) {i++;}
if ( (i <= x->num) && ( strcmp(val, x->chave[i]) == 0 ) ) {printf ("\nValor encontrado!\n");}
else {
if (x->folha == 1) {printf ("\nValor NAO encontrado!\n");}
else {
C = (Nod*)realloc(C,sizeof(Nod));
fseek(arqb,x->c[i],SEEK_SET);
fread(C,sizeof(Nod),1,arqb);
lt++;
busca_b(C,val);
//free(C);
}
}
}
void cria_b() { // cria arvore B //create the B-tree
long int i = 0;
char V[11];
raiz->folha = 1;
raiz->num = 0;
raiz->pos = 0;
for (i=1;i<=(Max+1);i++) {raiz->c[i] = -1;}
fseek(arqb,raiz->pos,SEEK_SET);
fwrite(raiz,sizeof(Nod),1,arqb);
rewind(arqt);
for (i=0;i<fSize(arqt);i++) {
fscanf(arqt,"%s\n",V);
insere_b(V);
}
}
答案 0 :(得分:2)
代码有点难读,但这就是我所看到的:
R = (Nod*)realloc(R,sizeof(Nod));
R = raiz;
您正在进行内存分配并立即丢弃结果。 realloc的结果可能与原始指针不同。
您可能需要一个函数来初始化树节点以使其更清晰 - 我很难遵循代码。
我期待看到树中每个节点的指针,但我没有看到任何指针。我没有关注你的实施。我建议在纸上画你的树,用箭头表示什么指向什么,并考虑所有角落的情况(例如当树空的时候第一次插入),因为那些通常是人们可能被卡住的东西。还可以使用调试器逐步执行代码,看看它是否按照您期望的方式运行。
编辑:如果整个树都是在一个文件中构建的(盯着这看起来似乎更多),你可能根本不需要进行任何动态内存分配。
通常你想要:
某些C / C ++编译器可以在运行时(例如Visual Studio)进行额外检查,这可能有助于查明问题。
另请参阅Jonathan的好评和惯例。
答案 1 :(得分:1)
头文件应该只包含结构,typedef,枚举,定义和函数声明的声明。你可以想象它中有内联函数定义,但可能没有。
您的头文件中包含完整的函数 - 不太可能内联并且未声明内联的函数。这意味着它无法用于其预期目的 - 使用相应C文件中定义的代码向文件提供声明。
代码不易阅读 - 而且问题不是语言。构造如:
while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {i--;}
不是惯用的C.正常的写作方式是:
while (i >= 1 && strcmp(val, x->chave[i]) < 0)
i--;
有编码指南要求在所有循环体和条件周围使用括号;如果您遇到其中一个,那么您可以使用以下几种编码约定之一:
while (i >= 1 && strcmp(val, x->chave[i]) < 0)
{
i--;
}
while (i >= 1 && strcmp(val, x->chave[i]) < 0) {
i--;
}
我非常喜欢前者;有很多人喜欢后者。
这个世界上除了Windows和Linux之外还有其他系统。
//defs of pre-compiler
#ifdef __linux
#define PAUSA "read p"
#define CLRSCR "clear"
#else
#define PAUSA "Pause"
#define CLRSCR "cls"
#endif
这对我的环境来说并不是特别有效 - MacOS X.我并不完全热衷于使用系统语句的程序,但我想这是一个问题,我是一个老用的人,不使用和IDE (虽然我不使用IDE的原因之一是因为命令行程序不能在其中运行,我主要是编写命令行程序,因此它们对我的正常工作模式不利。)
一个主要的性能错误是:
for (i = 0; i < fSize(arqt); i++)
这会在每次迭代时调用fSize()
函数,函数遍历整个文件,确定文件中有多少行,在返回之前恢复读取位置。目前尚不清楚您是否真的需要计算行数 - 您可以随时阅读这些行。但是,如果您确实需要对行进行计数,请执行一次。
int lines = fSize(arqt);
for (i = 0; i < lines; i++)
您可以使用以下几种方式进行循环:
for (x = 1; x <= MAX; x++)
这在C中通常是不正确的;数组索引从零开始,循环的规范形式为:
for (x = 0; x < MAX; x++)
在风格上,您通常会保留#define
或enum
值的所有大写名称,而不是常规变量。
在insere_b()
函数中,您有:
//B-TREE-INSERT //i believe the problem is here!
void insere_b(char *val)
{
Nod *S = NULL, *R = NULL;
R = (Nod*)realloc(R, sizeof(Nod));
//R = raiz;
if (R->num == Max)
{
S = (Nod*)realloc(S, sizeof(Nod));
其他人指出R = raiz;
行是可疑的。您将null指定给R
;然后你realloc()
它。这始终等同于malloc()
。此外,分配的内存未初始化,因此您可以获得随机值。这会导致问题。
然后,您使用S
(通过realloc()
分配内存)执行类似的序列,但这次您随后初始化已分配结构的部分(可能全部)。
这些足以引起麻烦。
当代码第一次进入b_split()
时,您会遇到位置值问题。具体来说,y->pos
为零,但x->pos
也是如此,因此程序以相同的偏移量存储(写入)两个节点的数据,这很少是幸福的秘诀。由于x
是根节点(至少在此上下文中 - 第一次拆分),因此它应位于0位置; y
和z
都需要处于不同的位置。 y
节点还包含非归零密钥,但这并不重要(除了整洁的目的),因为y->num
值表示它们未被使用。
您也不会使用chave[0]
键值AFAICT。这有点浪费。