首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我在下面的代码中遇到了分段错误?

为什么我在下面的代码中遇到了分段错误?
EN

Stack Overflow用户
提问于 2018-02-14 04:52:18
回答 1查看 112关注 0票数 0

我有一个txt文件,我只想得到超过12个字符的行。然后将这些行插入类型为graph_node的称为graph的变量中。

txt文件:

代码语言:javascript
复制
1,"execCode(workStation,root)","OR",0
2,"RULE 4 (Trojan horse installation)","AND",0
3,"accessFile(workStation,write,'/usr/local/share')","OR",0
4,"RULE 16 (NFS semantics)","AND",0
5,"accessFile(fileServer,write,'/export')","OR",0
6,"RULE 10 (execCode implies file access)","AND",0
7,"canAccessFile(fileServer,root,write,'/export')","LEAF",1
6,7,-1

图形节点类型:

代码语言:javascript
复制
#ifndef Graph_Structure
#define Graph_Structure

struct Graph_Node{

    char id[50];
    char node_name[50];

    struct Graph_Node* next;
    struct Graph_Node* edges;

    };  

typedef struct Graph_Node graph_node;


#endif   

这是将数据插入图形变量的方法:

代码语言:javascript
复制
void insert_node(graph_node** node, graph_node* data){

    printf("\nINSERTING\n");

    graph_node* temp = (graph_node*)malloc(sizeof(graph_node));

    for(int i = 0; i < strlen(data->id); i++)
        temp->id[i] = data->id[i];

    for(int i = 0; i < strlen(data->node_name) - 1; i++)
        temp->node_name[i] = data->node_name[i];

    temp -> next = *node;
    *node = temp;

    }

下面是获取txt文件中超过12个字符的行的方法:

代码语言:javascript
复制
void generate_nodes(graph_node** graph, char* file_name){

    graph_node* data = (graph_node*)malloc(sizeof(graph_node));

    FILE* f_Data_Pointer = fopen(file_name, "r");
    FILE* f_Aux_Pointer = fopen(file_name, "r");

    char c = 0; char line[256];

        int counter = 0; 
        int q = 0; //quotation marks

        bool jump_line = false;

    while(!feof(f_Data_Pointer)){

        c = 0; memset(line, 0, sizeof(line));

        while(c != '\n' && !feof(f_Aux_Pointer)){ // check line

            c = fgetc(f_Aux_Pointer); 
            line[counter] = c;
            counter++;

        }

        if(strlen(line) > 12){ //lines with no edges  

         /*line[counter-3] != '-' && line[counter-2] != '1'*/

         int size = strlen(line); printf("\nline size: %d\n", size);

            counter = 0; c = 0;

            while(c != ','){ //id

                c = fgetc(f_Data_Pointer);

                if(c != ','){

                    data->id[counter] = c;          
                    counter++;
                }

                printf("%c", c);

            }


            counter = 0; c = 0;

            while(1){ //node_name

                c = fgetc(f_Data_Pointer);

                if(c != '"'){

                data->node_name[counter] = c;
                counter++;

                } 

                else{

                    q++;                
                }

                if(q > 1 && c == ',')
                    break;

                printf("%c", c);

            }

            counter = 0; c = 0; 

            while(c != '\n'){ 

                c = fgetc(f_Data_Pointer);
                printf("%c", c);

            }

            insert_node(graph, data);
            memset(data->id, 0, sizeof(data->id));
            memset(data->node_name, 0, sizeof(data->node_name));

        }

        else{ //lines with edges

            while(c != '\n' && !feof(f_Data_Pointer)){ 

                c = fgetc(f_Data_Pointer);

            }

        }

    }

    fclose(f_Data_Pointer);
    fclose(f_Aux_Pointer);

}

我在"strlen“中的"for”命令的insert方法中得到了错误,它说data->id和data->node_name没有初始化,但我不明白为什么。我在数据上使用了malloc:

代码语言:javascript
复制
graph_node* data = (graph_node*)malloc(sizeof(graph_node));

错误:

代码语言:javascript
复制
Conditional jump or move depends on uninitialised value(s) ==3612== at 0x4C30B18: strcpy (vg_replace_strmem.c:510) ==3612== by 0x4008B2: insert_node (mulval.c:44) ==3612== by 0x400C03: generate_nodes (mulval.c:159) ==3612== by 0x400CE8: main (mulval.c:187)
EN

回答 1

Stack Overflow用户

发布于 2018-02-14 05:49:08

代码中最大的问题是,您经常忽略'\0'-terminating字节,并传递给像strlen这样的函数,它需要一个有效的字符串(以0结尾的字符串)。

例如在insert_node

代码语言:javascript
复制
for(int i = 0; i < strlen(data->id); i++)
        temp->id[i] = data->id[i];

这里你正在复制除字符串以外的所有字符,temp->id将存储一个字符序列,但它是a string。strlen(data->id)将具有未定义的行为,因为如果您在初始化data->id时没有对字符串进行0结尾,那么它很可能不会以0结尾。

在这里,如果您知道源字符串的长度小于49个字符,则应使用strcpy,或者使用strncpy以确保完全确定:

代码语言:javascript
复制
strncpy(temp->id, data->id, sizeof temp->id);
temp->id[sizeof(temp->id) - 1] = 0;

node_name也是如此。此外,您也没有检查malloc是否返回了NULL

此外,请参阅Why while(!foef(...)) is always wrongfoef线路也是错误的。

您应该重写此代码

代码语言:javascript
复制
while(c != '\n' && !feof(f_Aux_Pointer)){ // check line
    c = fgetc(f_Aux_Pointer); 
    line[counter] = c;
    counter++;
}

如下所示:

代码语言:javascript
复制
int c; // <-- not char c

while((c = fgetc(f_Aux_Pointer)) != '\n' && c != EOF)
    line[counter++] = c;

line[counter] = 0;  // terminating the string!

fgetc返回一个int,而不是char,它应该是int,否则与EOF的比较会出错。在这里,您忽略了设置0终止字节,结果是一个字符序列,而不是一个字符串。由于strlen将继续查找超出限制的0终止字节,因此下一行if(strlen(line) ...溢出。对于我来说,甚至不清楚为什么要检查EOF,因为当外部while循环恢复时,您会继续阅读。如果f_Aux_Pointer到了结尾,函数不应该直接返回吗?我不太清楚你在那里做什么。此外,对于换行符不在前49个已读字符中,也没有相应的策略。我认为你应该重新考虑你的策略。

这样做也会更容易:

代码语言:javascript
复制
if(fgets(line, sizeof line, f_Aux_Pointer) == NULL)
{
    // error handling
    return; // perhaps this?
}

line[strcspn(line, "\n")] = 0; // removing the newline

if(strlen(line) > 12)
...

这里

代码语言:javascript
复制
while(c != ','){ //id

    c = fgetc(f_Data_Pointer);

    if(c != ','){

        data->id[counter] = c;          
        counter++;
    }

    printf("%c", c);

}

您有与上面相同的错误,您从未设置\0-终止字节。在while的末尾,您应该有data->id[counter] = 0;。在下一个while循环中也是如此。

generate_nodes中,您不需要为临时graph_node对象分配内存,因为insert_node无论如何都会创建一个副本。您可以执行以下操作:

代码语言:javascript
复制
graph_node data;

...
data.id[counter] = c;
...
data.node_name[counter] = c;
...

insert_node(graph, &data);
data.id[0] = 0;
data.node_name[0] = 0;

这样你就少了一个malloc的麻烦了。

编辑

因为您的ID始终是数字的,所以我将结构更改为:

代码语言:javascript
复制
struct Graph_Node{
    int id;
    char node_name[50];

    struct Graph_Node* next;
    struct Graph_Node* edges;
};  

这会让生活变得更轻松。如果您的条件是只有您需要的行长于12个字符,并且您感兴趣的行始终与答案中发布的行的格式相同(逗号之间没有空格,第二列始终是引号),那么您可以这样解析它:

代码语言:javascript
复制
int generate_nodes(graph_node **graph, const char *file_name)
{
    if(file_name == NULL || graph == NULL)
        return 0; // return error

    // no need to allocate memory for it
    // if the insert_node is going to make a
    // copy anyway
    struct Graph_Node data = { .next = NULL, .edges = NULL };

    FILE *fp = fopen(file_name, "r");
    if(fp == NULL)
    {
        fprintf(stderr, "Error opening file %s: %s\n", file_name,
                strerror(errno));
        return 0;
    }

    // no line will be longer than 1024
    // based on your conditions
    char line[1024];

    size_t linenmr = 0;

    while(fgets(line, sizeof line, fp))
    {
        linenmr++;
        // getting rid of the newline
        line[strcspn(line, "\n")] = 0;

        if(strlen(line) <= 12)
            continue; // resume reading, skipt the line

        char *sep;
        long int id = strtol(line, &sep, 0);

        // assuming that there is not white spaces
        // before and after the commas
        if(*sep != ',')
        {
            fprintf(stderr, "Warning, line %lu is malformatted, '<id>,' exepcted\n", linenmr);
            continue;
        }

        data.id = id;

        // format is: "....",

        if(sep[1] != '"')
        {
            fprintf(stderr, "Warning, line %lu is malformatted, \"<string>\", exepcted\n", linenmr);
            continue;
        }

        // looking for ",
        char *endname = strstr(sep + 2, "\","); 

        if(endname == NULL)
        {
            fprintf(stderr, "Warning, line %lu is malformatted, \"<string>\", exepcted\n", linenmr);
            continue;
        }

        // ending string at ",
        // easier to do strcnpy
        *endname = 0;

        strncpy(data.node_name, sep + 2, sizeof data.node_name);
        data.node_name[sizeof(data.node_name) - 1] = 0;

        insert_node(graph, &data);
    }

    fclose(fp);
    return 1;
}

现在有趣的部分是:

代码语言:javascript
复制
    char *sep;
    long int id = strtol(line, &sep, 0);

    // assuming that there is not white spaces
    // before and after the commas
    if(*sep != ',')
    {
        fprintf(stderr, "Warning, line %lu is malformatted, '<id>,' exepcted\n", linenmr);
        continue;
    }

strtol是一个将字符串中的数字转换为实际整数的函数。此函数比atoi更好,因为它允许您将不同基数的数字从二进制转换为十六进制。第二个优点是它会告诉你它在哪里停止阅读。这对于错误检测非常有用,我使用此行为来检测该行是否具有正确的格式。如果行的格式正确,则必须在数字旁边显示逗号,,如果不正确,则打印一条错误消息并跳过该行,继续读取文件。

下一个if检查下一个字符是否是引号",因为根据您的文件,第二个参数包含在引号中。遗憾的是,参数的分隔符是逗号,它也用作字符串中的普通字符。这使得事情变得有点复杂(这里不能使用strtok )。再一次,我假设整个争论都以",结束。请注意,这不会考虑转义引号。像这样的一行:

代码语言:javascript
复制
3,"somefunc(a,b,\"hello\",d)","OR",0

会被错误地解析。当找到",时,我将引号设置为'\0',以便更容易地使用strncpy,否则我将不得不计算字符串的长度,检查它的长度是否没有超过目标大小,等等。如果您需要继续解析,endname + 1应该指向下一个逗号,否则格式错误。

代码语言:javascript
复制
    strncpy(data.node_name, sep + 2, sizeof data.node_name);
    data.node_name[sizeof(data.node_name) - 1] = 0;

在这里,我复制字符串。源是sep + 2,因为sep指向第一个逗号,所以您必须跳过它。下一个字符是引号,因此您也必须跳过它,因此使用sep + 2。因为名称的长度是未知的,所以最好使用strncpy,这将确保您不会写入超过所需的字节。

最后一步是将节点插入到图中。请注意,我没有分配内存,因为我知道insert_node无论如何都会创建一个新的副本。由于data仅供临时使用,因此不需要分配内存,只需将指向data的指针(通过传递&data)传递给insert_node即可。

我已经用一个虚拟graph和一个仅打印节点的虚拟insert_node测试了这个函数:

代码语言:javascript
复制
void insert_node(graph_node** node, graph_node* data)
{
    printf("ID: %d, node_name: %s\n", data->id, data->node_name);
}

这是输出:

代码语言:javascript
复制
ID: 1, node_name: execCode(workStation,root)
ID: 2, node_name: RULE 4 (Trojan horse installation)
ID: 3, node_name: accessFile(workStation,write,'/usr/local/share')
ID: 4, node_name: RULE 16 (NFS semantics)
ID: 5, node_name: accessFile(fileServer,write,'/export')
ID: 6, node_name: RULE 10 (execCode implies file access)
ID: 7, node_name: canAccessFile(fileServer,root,write,'/export')

现在,如果你使用这段代码,并且不断得到valgrind错误,那么这意味着你在代码的其他部分仍然有错误,你还没有给我们展示。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48775429

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档