【问题标题】:why am I getting segmentation fault on the code below?为什么我在下面的代码中出现分段错误?
【发布时间】:2018-02-14 09:46:24
【问题描述】:

我有一个 txt 文件,我只想获取大于 12 个字符的行。然后将这些线插入到名为 graph_node 类型的 graph 的变量中。

txt 文件:

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

图形节点类型:

#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   

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

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个字符的行的方法:

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:

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

错误:

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)

【问题讨论】:

  • 请发布显示问题的Minimal, Complete, and Verifiable example。如果没有双行间距,它会更具可读性,但有合理的空白。
  • insert_node 中,您正在复制所有字符,除了以 0 结尾的字符。使用strcpystrcpy(tmp-&gt;id, data-&gt;id);
  • 你听说过strcpy/strncpy吗? :)
  • 始终测试来自fopen 的返回值。还有char c; ==> int c; 总是阅读手册页。

标签: c fault


【解决方案1】:

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

例如在insert_node:

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

在这里,您正在复制除'\0'-终止字节之外的所有字符, temp-&gt;id 将存储一系列字符,但它不是字符串。 strlen(data-&gt;id) 将具有未定义的行为,因为 data-&gt;id 是最 如果您在没有以 0 结尾的字符串的情况下对其进行初始化,则可能不会以 0 结尾。

如果您知道源字符串小于 长度为 49 个字符,或者完全确定为 strncpy

strncpy(temp->id, data->id, sizeof temp->id);
temp->id[sizeof(temp->id) - 1] = 0;

node_name 相同。此外,您没有检查 malloc 是否返回 NULL.

还有foef 行是错误的,请参阅Why while(!foef(...)) is always wrong

你应该重写这个

while(c != '\n' && !feof(f_Aux_Pointer)){ // check line
    c = fgetc(f_Aux_Pointer); 
    line[counter] = c;
    counter++;
}

像这样:

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 终止字节,结果是字符序列,但不是字符串。 下一行 if(strlen(line) ... 因为这个而溢出,因为 strlen 将 继续寻找超出限制的 0 终止字节。对我来说不清楚 为什么你还要检查EOF,因为当外部while 时你会继续阅读 循环继续。如果f_Aux_Pointer 走到了尽头,该功能不应该只是 返回?我不太确定你在那里做什么。也没有策略 当换行符不在前 49 个读取字符中时。我觉得你应该 在这里重新考虑您的策略。

这样做也会更容易:

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)
...

这里

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

    c = fgetc(f_Data_Pointer);

    if(c != ','){

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

    printf("%c", c);

}

您遇到与上述相同的错误,您从未设置 \0 终止字节。在最后 在while 中,您应该拥有data-&gt;id[counter] = 0;。同样的事情在 下一个while 循环。

generate_nodes 中,您不需要为时间分配内存 graph_node 对象作为 insert_node 无论如何都会创建一个副本。你可以这样做:

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 始终是数字,所以我会将结构更改为:

struct Graph_Node{
    int id;
    char node_name[50];

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

这将使生活更轻松。如果您的条件为真 只有您需要的行长于 12 个字符,并且这些行 您感兴趣的格式始终相同(逗号之间没有空格, 第二列始终是引号)作为发布在您的 回答,那么你可以这样解析:

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

现在有趣的是这些:

    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 检查下一个字符是否是引号",因为根据您的 文件中,第二个参数用引号括起来。可悲的是分隔符 arguments 是一个逗号,也用作字符串中的普通字符。 这让事情变得有点复杂(你不能在这里使用strtok)。 再一次,我假设整个 参数以", 结尾。请注意,这不考虑转义 引号。像这样的一行:

3,"somefunc(a,b,\"hello\",d)","OR",0

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

    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 的指针(通过传递 &amp;data)传递给 insert_node

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

void insert_node(graph_node** node, graph_node* data)
{
    printf("ID: %d, node_name: %s\n", data->id, data->node_name);
}

这是输出:

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 错误,那么这意味着 您的代码的其他部分仍然存在未显示的错误 我们。

【讨论】:

  • 感谢您的回答。 aux_pointer 是首先检查该行是否小于 12 个字符,因为我不想将它们添加到图形变量中。并且 graph->node_name 永远不会得到换行符。
  • 最后一部分没看懂。你的意思是改变“graph_node* data = (graph_node*)malloc(sizeof(graph_node));”到“graph_node 数据;”所以它不再是一个指针,而且我不需要使用 malloc?
  • 我进行了更改,但仍然遇到分段错误。我没有做的唯一改变是最后一个,因为我不明白
  • 所以它不再是一个指针,而且我不需要使用 malloc? 是的,这是正确的。由于不是指针,它是堆栈上的一个对象。 ---- 但我仍然遇到分段错误 我仍然无法理解您对数据的解析。您能否编辑您的问题并显示输入文件的几行并解释这些行的含义,哪些是您想要的,哪些不是。有时您会寻找逗号,我不确定这是否正确,我不知道您的算法。
  • 是的,这是正确的我寻找一个逗号,因为我想要第一个逗号之前的 id 而不是第一个逗号之后的节点名称。但我不想要只有数字后跟逗号的行
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-24
  • 1970-01-01
  • 2010-10-19
相关资源
最近更新 更多