使用C中的文件实现的B树(不是B + / B *)中的问题

时间:2011-04-16 19:00:51

标签: c file-io segmentation-fault free b-tree

我是新来的,首先,如果我犯错误,我想道歉。我的问题是:我想在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);
}
}

2 个答案:

答案 0 :(得分:2)

代码有点难读,但这就是我所看到的:

R = (Nod*)realloc(R,sizeof(Nod));
R = raiz;

您正在进行内存分配并立即丢弃结果。 realloc的结果可能与原始指针不同。

您可能需要一个函数来初始化树节点以使其更清晰 - 我很难遵循代码。

我期待看到树中每个节点的指针,但我没有看到任何指针。我没有关注你的实施。我建议在纸上画你的树,用箭头表示什么指向什么,并考虑所有角落的情况(例如当树空的时候第一次插入),因为那些通常是人们可能被卡住的东西。还可以使用调试器逐步执行代码,看看它是否按照您期望的方式运行。

编辑:如果整个树都是在一个文件中构建的(盯着这看起来似乎更多),你可能根本不需要进行任何动态内存分配。

通常你想要:

  • 注意溢出,例如你写的是超出可用/已经分配的内存位置,字符串函数容易受到这些影响,请确保在需要的地方总是有一个有效的零终止字符串,并且你总是有足够的空间容纳你的字符串(就是它不超过你可以拥有的数量。)
  • 检查内存分配函数的NULL返回值。
  • 确保你没有泄漏内存,即分配和丢弃指针,从不调用free()。​​
  • 确保您总是使用先前分配的指针调用free,而不是在以后使用该指针。
  • 似乎有很多地方你在做realloc()但是真的想要malloc()。

某些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++)

在风格上,您通常会保留#defineenum值的所有大写名称,而不是常规变量。


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位置; yz都需要处于不同的位置。 y节点还包含非归零密钥,但这并不重要(除了整洁的目的),因为y->num值表示它们未被使用。

您也不会使用chave[0]键值AFAICT。这有点浪费。