迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C语言 >

C 段错误 Segmentation Fault

作者:迹忆客 最近更新:2023/03/29 浏览次数:

本篇文章将讨论 C 中的分段错误,并展示一些代码示例来解释此错误的原因。 首先,我们将讨论程序段和动态内存。

稍后,我们将探讨分段错误的不同原因和可能的解决方案。


C 中的程序段

计算机内存分为主内存和辅助内存。 它必须加载主内存 (RAM) 中的每个程序才能执行程序。

程序被进一步划分成不同的片段。

程序有五个主要部分。 这些细分是:

  1. 文本段 - 文本段包含程序的代码。
  2. 初始化数据段——初始化数据段包含程序的全局初始化变量。
  3. 未初始化的数据段——未初始化的数据段包含程序的全局未初始化变量。 此部分也称为 bss(更好地节省空间)。
    有时,我们声明一个大的未初始化数组,如 int x[1000],需要 4000 个字节; 但是,在初始化数组之前不需要此空间。
    因此,这个空间没有保留; 只保存一个指针。 初始化数组时分配内存。
  4. 堆 - 堆部分包含在运行时请求内存的数据,因为程序员在编码时不知道确切的大小。
  5. 栈 - 堆栈部分包含所有局部变量。 每当一个函数被调用时,它的局部变量就会被压入栈中(因此栈会增长),当函数返回时,局部变量会被弹出(栈会缩小)。

要可视化并更好地理解程序段,请参见以下代码:

int x = 5;
int y[100];
int main(){
    int number = 5;
    int *x = new int[5];
    return 0;
}

本程序中,x为全局初始化数据,y为全局未初始化数据。 接下来,数字是一个局部变量; 去一个堆栈区域。

x 是一个指针,也是一个局部变量,指向堆栈区域。 new int[5] 为堆区分配空间。

在 Unix 系列操作系统中,你可以很容易地看到这个程序的段。

C 中的程序段


C 中的动态内存

在许多程序中,程序员并不知道确切的内存需求。 在这种情况下,程序员要么从用户或文件中获取输入来获取数据大小,然后根据输入在运行时声明内存。

看一个例子:

int main(){
    int size;
    cout << "Enter Size: ";
    cin >> size
    int *x = (int*) malloc(size] * sizeof(int) );
    ...
    return 0;
}

在这个程序中,用户输入大小,程序在运行时根据大小分配内存。

在多道程序环境中,操作系统需要提供内存保护。 即限制程序在未经其自愿的情况下共享数据。

因此,每个操作系统都保留了一些机制来阻止程序访问非法内存。

我们只能访问为我们的程序保留的内存。 如果我们尝试访问程序地址空间之外的地址,或者程序分配的内存不足以满足动态分配请求,则可能会发生分段错误。

让我们详细讨论一下。


C 中的分段错误 - Segmentation Fault

当您尝试访问程序无法访问的内存位置或没有访问内存的权限时,就会发生分段错误。 让我们在下面讨论一些案例。

尝试引用未初始化的指针

此错误可能会造成混淆,因为某些编译器会针对某些情况发出警告并帮助您避免此错误,而其他编译器则不会。 下面是一个令人困惑的有趣示例。

int main(){
    int* pointer;
    printf("%d\n", *pointer);
    return 0;
}

在上面的代码中,我们试图取消引用一个未分配的指针,这意味着试图访问我们没有访问权限的内存。

使用 (cygwin) GCC 9.3.0 版编译此程序会出现分段错误(核心已转储)。

如果我们用 g++ 9.3.0 编译它,那么它会打印零。

现在,如果我们稍微改变一下这个程序并添加一个函数:

void access(){
    int* pointer;
    printf("%d\n", *pointer);
}
int main(){
    access();
    return 0;
}

现在两者都将打印垃圾值编译为输出,这令人困惑,因为我们仍在尝试访问未分配的内存。

如果我们在任何在线编译器上尝试这个,它都有类似的行为。 段错误异常; 有时,进行小的更改可以添加或删除此错误。

为避免此类错误,请记住初始化指针,并在取消引用之前检查指针是否不为空。

尝试分配大内存

此错误可能有两种方式。 一个是在堆栈上声明一个大数组,另一个是在堆上声明一个大内存。

我们将一一看到。

#include <stdio.h>

int main(){
    int largeArray[10000000]; // allocating memory in stack
    printf("Ok\n");
    return 0;
}

如果减少零的数量,输出将是 Ok; 然而,如果你继续增加零,在某些时候,程序会崩溃并给出:

Segmentation fault

原因是堆栈区域是有限的。 这意味着这个大数组所需的内存不可用。

最终,您的程序正试图超出该段。

如果我们需要更多内存(大于堆栈上的可用内存),我们可以使用堆。 但是,堆也有限制; 因此,如果我们不断增加内存大小,就会出现错误。

请参见下面的示例。

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *veryLargeArr;
    long int size = 100000000000;
    veryLargeArr = (int*) malloc (sizeof(int)*size);
    if(veryLargeArr == NULL)
        printf("Space is not enough.\n");
    else
        printf("memory allocation is successful\n");
    return 0;
}

输出结果:

memory allocation is successful

但是,如果我们继续增加大小,就会超出限制。 例如,我们遇到了以下大小的问题:

long int size = 1000000000000000;//100000000000

你可以在上面的语句中计算更多的零。 在这种情况下,程序可能会崩溃; 但是,为了避免分段错误,我们检查了指针是否为 NULL。

NULL 表示没有分配内存,因为请求的空间不可用。

输出结果:

Space is not enough.

如果您在不检查的情况下使用动态分配的内存来尝试此代码,您将遇到分段错误。

无限循环或递归调用

如果你错误地在你的程序中留下一个无限循环,那将导致段错误,特别是如果你在循环内分配动态内存。

给出了具有动态内存分配的无限循环的示例。

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *p;
    while (true)
        p = (int*) malloc(100000);
    return 0;
}

在这里,您可以看到使用 hile (true) 的无限循环。 循环内部的内存分配语句最终会产生错误,因为内存分配是反复重复的,而没有调用 free 方法释放内存。

同样,在不添加基本情况的情况下创建递归函数也会导致堆栈溢出。 请参见下面的示例。

void check(){
    check();
}

int main(){
    check();
}

在上面的代码中,检查函数将不断调用自身并在堆栈上创建它的副本,一旦程序的可用内存被消耗,这将导致段错误。


总结

当程序试图访问超出其范围或不可用的内存时,就会发生分段错误。 在取消引用之前检查指针是否指向任何内存。

如果需要大空间,则使用动态内存,并检查指针是否为 NULL。 确保在 scanf 中的变量之前使用 & 并在 printf 中的 % 之后使用正确的说明符。

不要尝试从数组中分配或访问超出其大小的任何值。 始终在声明时初始化变量和指针。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

在 C 语言中使用 typedef enum

发布时间:2023/05/07 浏览次数:181 分类:C语言

本文介绍了如何在 C 语言中使用 typedef enum。使用 enum 在 C 语言中定义命名整数常量 enum 关键字定义了一种叫做枚举的特殊类型。

C 语言中的静态变量

发布时间:2023/05/07 浏览次数:151 分类:C语言

本文介绍了如何在 C 语言中使用静态变量。在 C 语言中使用 static 变量在函数调用之间保存变量值

C 语言中生成随机数

发布时间:2023/05/07 浏览次数:64 分类:C语言

本文演示了如何在 C 语言中生成随机数。使用 rand 和 srand 函数在 C 语言中生成随机数

C 语言中的 i++ 与++i

发布时间:2023/05/07 浏览次数:83 分类:C语言

本文演示了如何在 C 语言中使用前缀增量与后缀增量运算符。C 语言中++i 和 i++ 记号的主要区别

C 语言中获取当前工作目录

发布时间:2023/05/07 浏览次数:182 分类:C语言

本文演示了如何在 C 语言中获取当前工作目录。使用 getcwd 函数获取当前工作目录的方法

C 语言中的位掩码

发布时间:2023/05/07 浏览次数:126 分类:C语言

本文介绍了如何在 C 语言中使用位掩码。使用 struct 关键字在 C 语言中定义位掩码数据

C 语言中的排序函数

发布时间:2023/05/07 浏览次数:181 分类:C语言

本文演示了如何在 C 语言中使用标准库排序函数。使用 qsort 函数对 C 语言中的整数数组进行排序

C 语言中的 extern 关键字

发布时间:2023/05/07 浏览次数:114 分类:C语言

本文介绍了如何在 C 语言中使用 extern 关键字。C 语言中使用 extern 关键字来声明一个在其他文件中定义的变量

C 语言中的 #ifndef

发布时间:2023/05/07 浏览次数:186 分类:C语言

本文介绍了如何在 C 语言中使用 ifndef。在 C 语言中使用 ifndef 保护头文件不被多次包含 C 语言中的头文件用于定义同名源文件中实现的函数的接口。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便