为什么 static 关键字在这里防止分段错误?
Why is the static keyword preventing a segmentation fault here?
我在 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()
有关。虽然我不知道那有什么问题...
谢谢!
未初始化(和零初始化)的静态变量被放入 BSS。 BSS 由加载程序清零。因此,通过使您的变量成为静态变量,您可以有效地将其初始化为 NULL
。如果没有 static ,则在堆栈上分配相同的变量。未初始化的堆栈变量可以包含任何随机值(取决于之前使用堆栈的内容)并且访问此类变量会导致未定义的行为。
您没有明确初始化 myList
变量。
当声明为局部(自动)变量时,它最初包含垃圾值,这就是当您尝试从堆栈中弹出所有内容时导致崩溃的原因。
当您将其声明为 static
时,它会为您使用空指针值隐式初始化,最终使您的弹出循环按预期终止。
静态变量默认自动初始化为0
,所以当你使用static
关键字时,mylist
的初始值将是一个空指针。然后当你循环遍历列表的元素时,while (mylist != NULL)
会在你到达这个空指针时停止。
自动变量不会自动初始化,所以mylist
的初始值是垃圾。 while
循环不会将此检测为列表的末尾,当您尝试通过此进行间接访问时,会发生未定义的行为。
要使用自动变量获得相同的行为,只需指定初始值:
LinkedList *myList = NULL;
我在 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()
有关。虽然我不知道那有什么问题...
谢谢!
未初始化(和零初始化)的静态变量被放入 BSS。 BSS 由加载程序清零。因此,通过使您的变量成为静态变量,您可以有效地将其初始化为 NULL
。如果没有 static ,则在堆栈上分配相同的变量。未初始化的堆栈变量可以包含任何随机值(取决于之前使用堆栈的内容)并且访问此类变量会导致未定义的行为。
您没有明确初始化 myList
变量。
当声明为局部(自动)变量时,它最初包含垃圾值,这就是当您尝试从堆栈中弹出所有内容时导致崩溃的原因。
当您将其声明为 static
时,它会为您使用空指针值隐式初始化,最终使您的弹出循环按预期终止。
静态变量默认自动初始化为0
,所以当你使用static
关键字时,mylist
的初始值将是一个空指针。然后当你循环遍历列表的元素时,while (mylist != NULL)
会在你到达这个空指针时停止。
自动变量不会自动初始化,所以mylist
的初始值是垃圾。 while
循环不会将此检测为列表的末尾,当您尝试通过此进行间接访问时,会发生未定义的行为。
要使用自动变量获得相同的行为,只需指定初始值:
LinkedList *myList = NULL;