【问题标题】:Why is this segmentation fault inconsistent across builds?为什么这个分段错误在构建之间不一致?
【发布时间】:2011-05-26 10:37:32
【问题描述】:

我写了一个 c 程序,编译它并且运行良好。经过几次编译 - 它开始给我一个分段错误。我重命名了文件夹,重新编译,它再次工作。
这是正常的吗?有不一致的分段错误?我更改了输出名称,更改文件夹名称等。它从给出分段错误反弹到不给出段错误。我不知道该怎么办了。
我的意思是,如果是编码问题,段错误应该是一致的,对吧?我每次都应该得到它。这是代码:
文件 my_set.c:

#include <stdio.h>
#include <stdlib.h>
#include "list.h"

/*
The program acceps a set of numbers from stdin until EOF
And then prints them (not storing duplicate numbers)
*/

int main ()
{
    int num; 
    nodePtr head; /*head of the list*/

    while (scanf("%d", &num) != EOF)
    {
        addToList(num, &head);
    }
    printList(head);
    freeList(head);
    return 0;
}

文件列表.c:

#include <stdio.h>
#include <stdlib.h>
#include "list.h"

/*
Implements a linked list, each element of which contains a dynamic array.
I used a linked list to maximize potential memory in case it is fragmented.
I use a dynamic array in each node to minimize the percentage of overhead
from creating a list (the pointer, the index...);
*/

/*
Adds number n to list *h
4 cases:
1. list is empty:
    creating one
    updating h with new list
    creating a new dynamic array in the list
    updating it and the index
2. can reallocate current node's array for 1 more int
3. cannot reallocate current node's array:
    creating a new node
    initializing it
4. cannot create a new node
    printing the current list, an "out of memory error" and freeing all memory.
*/
void addToList(int n, nodePtr *h)
{
    static nodePtr p; /*points to current last node*/
    int *temp; /*for use in reallocation*/

    if (!*h) /*first item of set*/
    {
        *h = malloc (sizeof(node));
        (*h)->arr = malloc(sizeof(int));
        (*h)->arr[0] = n;
        (*h)->i = 1;
        p = *h;
        return;
    }

    /*if n is already in the list, no need to add it
    the call comes after first item, because first item cannot be in the list*/
    if(existsInList(n, *h)) return;

    /*using realloc while still possible*/
    if ((temp = realloc(p->arr, (p->i+1)*sizeof(int))))
    {
        p->arr = temp;
        p->arr[p->i] = n;
        p->i++;
        return;
    }

    /*if  realloc no longet possible - start new node*/
    if ((p->next = malloc(sizeof(node))))
    {
        p = p->next;
        p->arr = malloc(sizeof(int));
        p->arr[0] = n;
        p->i = 1;
        return;
    }

    /*can no longer start new nodes - quit with error, after printing*/
    printf("out of memory!");
    printList(*h);
    freeList(*h);
}

/*checks if n is in p assuming p is not null
it can asume so because the call for it comes after the check for first item*/
int existsInList(int n, nodePtr p)
{
    int i;
    for (; p ; p = p->next)
        for (i = 0; i < p->i; i++)
            if (p->arr[i] == n)
                return 1;
    return 0;
}

/*frees the list*/
void freeList(nodePtr p)
{
    nodePtr temp = p;

    if (!p) return; /*list is empty*/

    while (p)
    {
        free(p->arr);
        p = p->next;
        free(temp);
    }
}

/*prints the content of the list to stdout*/
void printList(nodePtr p)
{
    if (!p) return;
    int i;
    printf("\n");
    for (; p ; p = p->next)
        for (i = 0; i < p->i; i++)
            printf("%d ", p->arr[i]);   
    printf("\n");
}

文件列表.h:

/*
pointer to a node
declare a variable of this type to create a list
then start adding to the list
*/
typedef struct s *nodePtr;

/*the struct that represents each node of the list
reason for dynamic array is in "list.c"
*/
typedef struct s
{
    int *arr;
    int i; /*index for next num, also size of array;*/
    nodePtr next;
}node;

/*Adds the int to list at nodePtr omitting duplicates*/
void addToList(int, nodePtr*);
/*prints a list*/
void printList(nodePtr);
/*returns 1 if an int exists in list referenced by nodePtr, 0 otherwise*/
int existsInList(int, nodePtr);
/*frees all dynamically allocated memory*/
void freeList(nodePtr);

基本上我所做的只是从标准输入中获取数字,将它们放入列表中(不重复),然后打印它们。我使用动态数组列表。

【问题讨论】:

    标签: c segmentation-fault


    【解决方案1】:

    初始化你的变量!

    int num = 0;  
    nodePtr head = NULL; /*head of the list*/
    

    添加:不一致的行为可能来自调试与发布编译,通常调试模式下的编译器将未初始化的变量设置为奇怪的值,如0xDDDDDDDD,以使问题立即可见。在释放模式下,如果内存块被清零,就会发生变量的内容为0但不能保证的情况。

    【讨论】:

    • 这似乎可以解决问题。我不知道指针未初始化。但我不认为它是调试/发布导致不一致,我每次编译都使用相同的 makefile。 (并且没有 -g 标志)
    • -g 标志告诉包含或不包含调试信息,但您确实进行了调试构建(没有优化)-O0(或很快-O)应该用作编译开关。在这些情况下,编译器还会报告更多警告。在这种情况下,可能也是未初始化的问题。您可以拥有带有调试信息的优化可执行文件,-g 仅告诉是否添加符号,而不是未优化的调试版本。还可以考虑使用-Wall 来启用所有警告。
    • 发布应该使用什么? -O3?
    【解决方案2】:

    您应该检查来自malloc() 的返回值,以防它返回NULL(内存不足)。

    【讨论】:

    • 唯一未检查的 malloc 位于 addToList 函数的第一个 if 中,它应该只运行一次,并且只分配一个 struct 和一个 int。检查多次运行的 realloc 和 malloc。
    • @Danish94 addToList() 的最后一个 if() {} 中有另一个未经检查的 malloc。
    【解决方案3】:

    c/c++ 程序中的间歇性段错误通常是由未初始化的内存引起的,通常在指针变量中。

    您发布了很多代码,这使得仅阅读它就很难进行调试。我建议仔细阅读代码,并在声明变量的任何地方给它一个初始值(例如零或 NULL)。请记住,编译器不会为您初始化它们。

    您应该首先在main() 中初始化numhead 的值。例如

    int num = 0; 
    nodePtr head = NULL; /*head of the list*/
    

    编辑 1

    另一个错误在addToList()。如果该函数中的第一个 if 块未执行,则当您稍后调用 realloc(p-&gt;arr, ...) 时,局部变量 p 的值将未初始化。当您取消引用 p 以获取 p-&gt;arr, ifp` 未初始化时,通常会出现段错误。

    编辑 2

    使用 C/C++ 编程时的两个有用技巧:

    1. 始终在声明变量时对其进行初始化。如果您不这样做,那么它们的值是未定义的。请注意,这并不能解决所有问题。如果你取消引用一个未初始化的指针,那么你通常会得到一个段错误。如果你将它初始化为 null 然后取消引用它,那么你总是会得到一个段错误。更容易调试,但它仍然会崩溃。
    2. 始终在代码中尽可能靠近您第一次使用它们的位置声明变量。这具有减少使用未初始化变量的机会的效果,因为编译器将生成“未声明的变量”错误。在函数开头声明所有变量的做法是旧式'K&R' C 的遗留问题,您必须这样做。现代 C 不需要它。

    所以,而不是:

    int foo()  // Warning: bad code
    {
        int a;
        int b;
    
        func1();
        a=func2(&b);
        return a;
    }
    

    尝试类似:

    int foo()
    {
        func1();
        int b = 42;
        int a = func2(&b);
        return a;
    }
    

    【讨论】:

    • 第一个if块的重点是在第一次调用方法时运行,从而初始化p。
    • @Danish94 我明白这一点。 addToList() 中的第一个 if 块只有在 h 指向的值为 null 时才会执行。假设您现在正在初始化head,那么这将在第一次调用addToList() 时发生。但是在随后对addToList() 的调用中,h 的值将是非空的,p 将不会被初始化,并且在调用realloc() 时会出现问题。
    • @AndyJohnson 但这就是静态的用途,为了在对 addToList 的多次调用中保留它,所以在后续调用中,p 仍然被初始化。
    • @Danish94 你是对的。我没有注意到 p 是静态的。那会奏效的。您仍然应该初始化 p:请参阅我的第二次编辑。
    • @AndyJohnson 这种做法在 c90 中是否可行,还是仅在 c99 中?
    猜你喜欢
    • 2016-10-25
    • 2017-08-18
    • 1970-01-01
    • 2015-06-12
    • 2016-09-24
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多