为啥静态关键字在这里防止了分段错误?

Posted

技术标签:

【中文标题】为啥静态关键字在这里防止了分段错误?【英文标题】:Why is the static keyword preventing a segmentation fault here?为什么静态关键字在这里防止了分段错误? 【发布时间】:2015-08-27 03:59:51 【问题描述】:

我在 C 语言中玩弄指针,遇到了我不理解的行为。它涉及到在以下程序中使用static 关键字:

/**
*      Simple LIFO stack implemented using linked lists
*/
#include <stdio.h>
#include <stdlib.h>

typedef struct LinkedList LinkedList;
struct LinkedList
        int value;
        LinkedList *next;
;

int pop(LinkedList **list)
        LinkedList *next;
        int ret;        
        ret = (*list)->value;
        next = (*list)->next;
        //free(*list);
        *list = next;
        return ret;

void push(LinkedList **list, int value)        
        LinkedList *new = (LinkedList*)malloc(sizeof(LinkedList));
        new->value = value;
        new->next = (*list);
        *list = new;


int main()         
        LinkedList *myList;

        for(int i = 0; i<10; i++)
                push(&myList, i);


        while(myList!=NULL)
                printf("popped %i\n", pop(&myList));          

        return 0;

当我编译并运行上面的代码时,我遇到了分段错误。但是,当我将 myList 的声明更改为 static LinkedList *myList 时,程序(看似)完美地工作:

output without static keyword:
popped 9
popped 8
popped 7
popped 6
popped 5
popped 4
popped 3
popped 2
popped 1
popped 0
popped 1
popped 1970220846
Segmentation fault (core dumped)

output with static keyword:
popped 9
popped 8
popped 7
popped 6
popped 5
popped 4
popped 3
popped 2
popped 1
popped 0

我真的不明白为什么会这样。我认为它与范围有关,因为以下代码无需static 关键字即可工作:

/**
*      Simple LIFO stack implemented using linked lists
*/
#include <stdio.h>
#include <stdlib.h>

typedef struct LinkedList LinkedList;
struct LinkedList
        int value;
        LinkedList *next;
;

int pop(LinkedList **list)
        LinkedList *next;
        int ret;        
        ret = (*list)->value;
        next = (*list)->next;
        //free(*list);
        *list = next;
        return ret;

void push(LinkedList **list, int value)        
        LinkedList *new = (LinkedList*)malloc(sizeof(LinkedList));
        new->value = value;
        new->next = (*list);
        *list = new;


int main()         
        LinkedList *myList;

        push(&myList, 0);
        push(&myList, 1);
        push(&myList, 2);
        push(&myList, 3);
        push(&myList, 4);
        push(&myList, 5);
        push(&myList, 6);
        push(&myList, 7);
        push(&myList, 8);
        push(&myList, 9);


        while(myList!=NULL)
                printf("popped %i\n", pop(&myList));          

        return 0;

我知道分段错误的原因是 pop() 函数试图取消引用错误地址(因为显然 myList!=NULL 检查不起作用),但由于某种原因 static 关键字神奇解决所有问题!

唯一想到的另一件事是,它与将 for 循环中的 myList 指针的引用传递给 push() 有关。虽然不知道这是怎么回事……

谢谢!

【问题讨论】:

'new' 是 C++ 中的关键字。当使用可以编译 C++ 程序的编译器时,这将是一个问题。建议将“新”改为更有意义的东西 在 C 中调用 malloc()(和相关的函数族)时,1) 不强制转换返回值。它已经是一个 void * 因此可以分配给任何其他指针。 2) 始终检查 (!=NULL) 返回值以确保操作成功 在 pop() 函数中,一旦头指针越过第一个结构实例,指向该结构的指针仅在该函数中是已知的。因此,必须在该函数内执行对“断开连接”结构实例的 free() 的调用。发布的代码有必要的声明注释。如果不调用 free(),就会发生内存泄漏 在函数中:main() 指针'myList'需要初始化为NULL。因为 myList->next 在第一次调用 push() 时没有设置为 NULL,而是设置为 myList 第一次放入堆栈时 myList 中的垃圾。所以循环遍历链表不会看到终止的 NULL。 【参考方案1】:

未初始化(和零初始化)的静态变量被放入 BSS。 BSS 由加载程序归零。因此,通过将变量设为静态,您实际上将其初始化为NULL。如果没有静态变量,则在堆栈上分配相同的变量。未初始化的堆栈变量可以包含任何随机值(取决于之前使用堆栈的内容),访问此类变量会导致未定义的行为。

【讨论】:

具有静态存储持续时间的对象被语言正确地初始化为零。 “BSS”和“loader”只是某些特定平台的细节。 C 语言对任何“BSS”或“加载程序”一无所知。【参考方案2】:

您没有显式初始化您的 myList 变量。

当声明为本地(自动)变量时,它最初包含垃圾值,当您尝试从堆栈中弹出所有内容时,这会导致崩溃。

当您将其声明为static 时,它会为您使用空指针值隐式初始化,最终使您的弹出循环按预期终止。

【讨论】:

【参考方案3】:

静态变量默认自动初始化为0,所以当你使用static关键字时,mylist的初始值将是一个空指针。然后当你遍历列表的元素时,while (mylist != NULL) 将在你到达这个空指针时停止。

自动变量不会自动初始化,所以mylist的初始值是垃圾。 while 循环不会将其检测为列表的末尾,并且当您尝试通过此进行间接处理时,会发生未定义的行为。

要获得与自动变量相同的行为,只需指定初始值:

LinkedList *myList = NULL;

【讨论】:

以上是关于为啥静态关键字在这里防止了分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这段代码在 leetcode 运行良好,但在 geeksforgeeks 出现分段错误?

为啥我的字符串分配会导致分段错误?

为啥调用 glCreateShader 时这个 OpenGL 着色器分段错误?

分段错误:为啥?

为啥写作主要;在 C 中给出一个段错误

为啥释放内存会导致分段错误?