【问题标题】:Problem in B-tree (not B+/B*) implemented with files in C用 C 中的文件实现的 B-tree(不是 B+/B*)中的问题
【发布时间】:2011-08-07 00:36:35
【问题描述】:

我是新来的,首先,如果我有问题,我想道歉。我的问题是:我想在 C 中实现 B 树,使用文件来存储树...我的程序从文本文件中读取 10000 个字符串,每个字符串包含 10 个字符,并将该内容存储在 .DAT 二进制文件中,通过 B-tree 组织;然后用户可以搜索一个字符串。

我正在使用“Cormen,et al - Introduction to Algorithms (3ed)”的算法,这似乎是正确、清晰和实用的。但是,我的程序只是出现运行时错误......比如分段错误和无限循环。我一直在尝试调试 5 天,但没有成功! B-tree 函数是递归的,我个人讨厌...我认为正是递归使调试变得如此困难!

我的代码比较大,分为2个文件,1个源,1个头。我将在这里发布 B-tree 的功能和变量声明。但是,如果有人想查看完整的代码,我会发布一个“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) 所有代码cmet都是我尝试解决问题的想法,必须认为我已经尝试过了。

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);
}
}

【问题讨论】:

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


    【解决方案1】:

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

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

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

    您可能需要一个函数来初始化您的树节点以使其更清晰 - 我很难按照代码进行操作。

    我希望在树中的每个节点中看到指针,但我没有看到任何指针。我没有关注你的实施。我建议在纸上画出你的树,用箭头显示什么指向什么,并考虑所有极端情况(例如,当树为空时首先插入),因为这些通常是人们可能会卡住的地方。还可以使用调试器单步调试您的代码,看看它的行为是否符合您的预期。

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

    通常你想要:

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

    一些 C/C++ 编译器可以在运行时进行额外检查(例如 Visual Studio),这可能有助于查明问题。

    还可以看看 Jonathan 的优秀 cmets 重新约定和风格。

    【讨论】:

    • 非常感谢您的评论家伙...我不明白“realloc”的东西...你能解释一下吗?关于内存分配,我想和你一样..可能我可以在没有任何指针的情况下制作这个程序,除了文件..你同意这个吗?
    • realloc,很多时候,表现为malloc,还是我错了?比如一个指针没有初始化的时候,如果使用realloc或者malloc,结果不一样?
    • @guipy:如果将 NULL 传递给 realloc,它的行为类似于 malloc,但会使读者感到困惑,因为我希望 realloc 用于调整分配内存的大小而不是分配固定数量的字节。是的,看起来你不需要任何指针......
    【解决方案2】:

    头文件应该只包含结构、类型定义、枚举、定义和函数声明的声明。您可能会在其中包含内联函数定义,但可能没有。

    您的头文件中包含完整的功能 - 不太可能内联且未声明为内联的功能。这意味着它无法用于其预期目的 - 使用相应 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-&gt;pos 为零,x-&gt;pos 也为零,因此程序在相同的偏移量处存储(写入)两个节点的数据,这很少是幸福的秘诀。由于x 是根节点(至少在这种情况下——第一次分裂),它应该在位置0; yz 都需要在不同的位置。 y 节点还包含非零键,尽管这无关紧要(为了整洁除外),因为 y-&gt;num 值表明它们没有被使用。

    您也从未使用过 chave[0] 键值 AFAICT。有点浪费。

    【讨论】:

    • 嘿乔纳森,我很欣赏你的建议,我知道你所说的“惯用 C”是什么意思。在我的情况下,我就像一个......“爱好者”,所以我写我喜欢的方式。特别是,我更喜欢 while( (cond1) && (cond2) ) {code}...就像我的想法一样,呵呵...关于最后一条评论:我的程序将只在 Linux 和 Dos 上运行,所以..Mac OS X 与我无关!无论如何,你有什么错误要指点我吗?
    • 哇,非常感谢!!在 fSize 的情况下,我同意你的看法。每次调用该函数真的是浪费时间! MAX 和 MIN,T 也是由#define 确定的常数。我的数组从 0 运行到 K+1,因为我的算法是从 1 到 K,所以我保留它们,浪费了数组的第一个元素。指针很危险,我现在就试试这个程序没有它们!如果运行,我会在这里告诉...再次感谢!
    • @guipy:在我的半工作代码中,我将insereb() 中的realloc() 行替换为Nod *R = raiz;
    • @Jonathan:为什么要半工作?有指针的错误,还是 B 树中的错误?再次感谢您的帮助...我正在尝试没有成功的指针...
    • @guipy:嗯,半工作,因为b_split() 工作不正常。但为了做到这一点,我修改了insere_b(),如上所述。我做了很多其他的修改。如果您想要我编辑的代码(请参阅我的个人资料),请通过电子邮件与我联系 - 但修改后的代码不是万能药,因为它至少仍然有一个损坏的 b_split()。我还没有弄清楚如何跟踪程序中的所有 B-Tree 节点。一旦你添加了第二个(和第三个)节点,我就遇到了问题。
    猜你喜欢
    • 2011-06-03
    • 2011-03-12
    • 2012-11-05
    • 2010-09-07
    • 2011-07-27
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2012-03-07
    相关资源
    最近更新 更多