什么是细分错误?在C和C ++中有区别吗?分段错误和悬空指针如何关联?

评论

分段错误使编译器感到不舒服。

如果是这样,为什么在我的情况下,编译器什么也没抱怨,一切都顺利进行,但是在运行时系统抛出了分段错误(核心转储)? T_T

发生问题时只是内存转储!

@pinouchon:有趣,但是编译器何时与seg错误有关?难道不是运行时环境吗?

分段来自内存分段。您正在访问不属于您的内存段。

#1 楼

分段错误是由访问“不属于您”的内存引起的特定类型的错误。这是一种帮助机制,可防止您破坏内存并引入难以调试的内存错误。每当遇到段错误时,您就知道您在内存上做错了–访问已被释放的变量,写入内存的只读部分,等等。在大多数语言中,分段错误在本质上都是相同的,这会让您陷入困境使用内存管理,C和C ++中的段错误之间没有主要区别。
至少有一些较低级的语言(例如C(++))有很多方法可以获取段错误。获取段错误的一种常见方法是取消引用空指针:
int *p = NULL;
*p = 1;

当您尝试写入标记为只读的内存部分时,会发生另一个段错误:
char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault
<悬空指针指向不再存在的对象,例如:
char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

指针p悬空是因为它指向在块结束后不再存在的字符变量c。当您尝试取消引用悬空指针(例如*p='A')时,可能会遇到段错误。

评论


在构建时,最后一个示例特别讨厌:int main(){char * p = 0; {char c ='x'; p =&c; } printf(“%c \ n”,* p);返回0;使用gcc或其他几个编译器,它“似乎”开始工作。没有关于编译的警告。没有段错误。这是因为'}'不在范围内,实际上并不会删除数据,只是将其标记为可以再次使用。该代码可以在生产系统上正常运行数年,您可以更改代码的另一部分,更改编译器或其他内容,然后再执行!

–克里斯·黄·利弗(Chris Huang-Leaver)
10-4-13在9:06



抱歉,碰到的只是一个侧面说明...您的示例都未必会导致段错误,实际上这只是未定义的行为;-)

– obataku
2012年9月15日下午3:01

@oldrinb:不可能编写必然导致段错误的代码。尤其是因为那里有没有内存保护的系统在运行,因此无法判断内存是否确实属于您,因此不知道段错误,只有不明确的行为……(例如,经典的AmigaOS)

–DevSolar
2014年5月29日在18:03



@ ChrisHuang-Leaver,您需要了解c是局部的,这意味着它在{之后被压入堆栈,并在}之后弹出。悬空指针只是对偏移量的引用,该偏移量现在已从堆栈中移出。这就是为什么在简单的程序中对其进行修改将永远不会触发任何段错误的原因。另一方面,在更复杂的用例中,这可能会导致段错误,在此情况下,其他函数调用可能导致堆栈增长并包含悬空指针指向的数据。写入该数据(本地变量)将导致未定义的行为(segfault&Co)

–艾曼·卡莫玛(Ayman Khamouma)
16年1月19日在21:23



@ ChrisHuang-Leaver,通常在超出范围时,编译器必须恢复一些堆栈空间以释放未使用的堆栈空间,但这并不总是发生的(gcc是其中一种编译器)。另外,分配的堆栈空间通常会再次被重用,所以我听说没有操作系统会将未使用的堆栈页面返回给系统,从而使该空间成为SIGSEGV的对象,因此我不会期望这样的信号来处理堆栈。

–路易斯科罗拉多
16年7月22日在11:59

#2 楼

值得注意的是,分段错误不是由直接访问另一个进程内存引起的(这是我有时听到的),因为这是不可能的。使用虚拟内存,每个进程都有自己的虚拟地址空间,无法使用任何指针值访问另一个进程。例外是共享库,它们是相同的物理地址空间,映射到(可能是)不同的虚拟地址,内核内存甚至在每个进程中都以相同的方式映射(我认为,这是为了避免在系统调用中进行TLB刷新)。还有类似shmat;)的东西-这些就是我所谓的“间接”访问。但是,可以检查它们是否通常位于距过程代码很远的地方,并且我们通常可以访问它们(这就是为什么它们在那里的原因,但是以不正确的方式访问它们会产生分段错误)。

仍然,如果以不正确的方式访问我们自己的(进程)内存(例如,尝试写入不可写的空间),则可能会发生分段错误。但是最常见的原因是访问虚拟地址空间中根本没有映射到物理地址的那一部分。

所有这些都与虚拟内存系统有关。 >

评论


使用共享的内存/内存映射文件,其他人可能会破坏您的内存。在WIN32中也有讨厌的API,例如“ WriteProcessMemory”!

– paulm
2014-2-17在23:46

@paulm:是的,我知道。这就是我在“以及类似shmat;)的想法中所想到的-这些就是我所谓的'间接'访问。”

– konrad.kruczynski
14年2月18日在10:08

在虚拟内存操作系统中,进程无法访问进程(通常,因此,请操作系统的实现者,请不要为此而烦恼),而不能通过某种内存附加系统调用来允许您执行以下操作:访问。虚拟内存地址通常取决于所考虑的进程而具有不同的含义。

–路易斯科罗拉多
16年7月22日在12:02

#3 楼

分段错误是由于对进程未在其描述符表中列出的页面的请求或对它确实已列出的页面的无效请求(例如,对只读页面的写请求)引起的。 />
悬空指针是可能指向也可能不指向有效页面的指针,但它确实指向内存的“意外”段。

评论


的确如此,但是如果您不知道细分错误是什么,那真的会对您有帮助吗?

– zoul
2010-2-27在9:37

#4 楼

老实说,正如其他海报所提到的那样,Wikipedia对此有很好的文章,因此可以在那里看看。这种类型的错误非常普遍,通常被称为“访问冲突”或“一般保护错误”之类的其他错误。

它们在C,C ++或任何其他允许指针的语言中没有什么不同。这些类型的错误通常是由以下指针引起的:


在正确初始化之前使用这些指针
在它们所指向的内存被重新分配或删除之后使用。
使用在索引数组位于索引超出数组范围的地方。通常,仅当您在传统数组或c字符串上进行指针数学运算时,而不是在C ++中基于STL / Boost的集合进行指针数学运算时才可以。


#5 楼

根据Wikipedia:

当程序尝试访问不允许访问的内存
或尝试访问内存时发生分段错误。
(例如,尝试写入
br />

#6 楼

分段故障也是由硬件故障(在这种情况下为RAM存储器)引起的。这是较不常见的原因,但是如果您在代码中未发现错误,也许memtest可以为您提供帮助。

在这种情况下,解决方案是更换RAM。

edit:

这里有一个参考:硬件分割错误

评论


对RAM出现故障的快速测试是在循环中一次又一次地运行崩溃的程序。如果程序没有内部不确定性,也就是说,它总是为相同的输入或至少应该产生相同的输出,但是对于某些特定的输入,它有时会崩溃,但并非总是崩溃,但也不会永远崩溃:那么您应该开始担心坏的RAM。

– zwol
17-10-26在21:39



#7 楼

Wikipedia的Segmentation_fault页面对此有很好的描述,仅指出了原因和原因。请查看Wiki以获取详细说明。

在计算中,分段错误(通常简称为segfault)或访问冲突是具有内存保护功能的硬件引发的故障,通知操作系统(OS) )关于内存访问冲突。

以下是导致分段错误的一些典型原因:


取消引用NULL指针–这是内存管理的特殊情况硬件
试图访问不存在的内存地址(进程的地址空间外)
试图访问内存的程序无权(例如进程上下文中的内核结构)
试图写读专用内存(例如代码段)

这些通常是由于编程错误导致无效的内存访问而引起的:


取消引用或分配给内存未初始化的指针(野生指针,指向随机内存地址)
解引用或分配给释放的指针(dan滑动指针,指向已释放/取消分配/删除的内存)
缓冲区溢出。
堆栈溢出。
试图执行无法正确编译的程序。 (尽管存在编译时错误,某些编译器仍将输出可执行文件。)


#8 楼

当某个进程(正在运行的程序实例)尝试访问只读内存地址或其他进程正在使用的内存范围或访问不存在(无效)的内存地址时,就会发生分段错误。
(指针)问题意味着尝试访问其内容已从内存中删除的对象或变量,例如:

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here


评论


删除数组的正确方法是delete [] arr;

–大面
16 Mar 28 '16 at 15:48

#9 楼

用简单的话来说:分段错误是操作系统向程序发送信号
,表明它已检测到非法的内存访问,并且过早地终止了该程序以防止
内存被破坏。

#10 楼

答案中有很多关于“分段错误”的很好的解释,但是由于分段错误经常有内存内容的转储,因此我想分享分段错误中的“核心转储”部分(核心转储)与内存之间的关系。内存来自:

从1955年到1975年-在半导体内存出现之前-计算机内存中的主导技术是在铜线上缠上微小的磁性圆环。甜甜圈被称为“铁氧体磁芯”,主存储器也因此被称为“磁芯存储器”或“磁芯”。


从这里获取。

#11 楼

“分段错误”表示您试图访问您无权访问的内存。

第一个问题是与main参数有关。主要功能应为int main(int argc, char *argv[]),并且在访问argv [1]之前应检查argc至少为2。

此外,由于您要将float传递给printf(顺便说一句) ,并在传递给printf时转换为double),则应使用%f格式说明符。 %s格式说明符用于字符串(以'\ 0'结尾的字符数组)。

#12 楼

当程序尝试访问不存在的内存位置或试图以不允许的方式访问内存位置时,就会发生分段错误或访问冲突。

 /* "Array out of bounds" error 
   valid indices for array foo
   are 0, 1, ... 999 */
   int foo[1000];
   for (int i = 0; i <= 1000 ; i++) 
   foo[i] = i;


i [1000]不存在,因此发生段故障。

分段错误的原因:

it arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

De-referencing NULL pointers – this is special-cased by memory management hardware.

Attempting to access a nonexistent memory address (outside process’s address space).

Attempting to access memory the program does not have rights to (such as kernel structures in process context).

Attempting to write read-only memory (such as code segment).


评论


首先,seg fault与地址是否存在无关。它是关于您正在不允许访问它的地方。在您的特殊示例中,甚至标准地保证了该位置的存在。因为该标准说的是数组形式的情况,所以必须给出在其边界AND 1之后的一个对齐良好的数组上的指针pointg的有效地址。

– dhein
2015年12月8日在16:25

如果您没有该地址,并且您尝试访问该地址,则还会有一个段。故障。在我的示例中,它仅用于理解观点。

– Mohit Rohilla
2015年12月12日,下午3:11

#13 楼

分段错误有足够的定义,我想引用一些编程时遇到的示例,这些示例看似愚蠢的错误,但会浪费很多时间。



在以下情况下可能会出现分段错误,而printf中的argumet类型不匹配

#include<stdio.h> int main(){ int a = 5; printf("%s",a); return 0; }


输出:Segmentation Fault (SIGSEGV)



,当您忘记为指针分配内存但尝试使用它时。

 #include<stdio.h> 
 typedef struct{
   int a;
 }myStruct;   
int main(){
  myStruct *s;
  /* few lines of code */
  s->a = 5;
  return 0;
}



输出:Segmentation Fault (SIGSEGV)

#14 楼

Segmentation fault的简单含义是您正在尝试访问一些不属于您的内存。当我们尝试在只读内存位置读取和/或写入任务或尝试释放内存时,会发生Segmentation fault。换句话说,我们可以将其解释为某种内存损坏。

下面我提到导致Segmentation fault的程序员所犯的常见错误。


以错误的方式使用scanf()(忘记放入&)。

int num;
scanf("%d", num);// must use &num instead of num



以错误的方式使用指针。

int *num; 
printf("%d",*num); //*num should be correct as num only
//Unless You can use *num but you have to point this pointer to valid memory address before accessing it.



修改字符串文字(指针尝试来写入或修改只读存储器。)

char *str;  

//Stored in read only part of data segment
str = "GfG";      

//Problem:  trying to modify read only memory
*(str+1) = 'n';



尝试访问已释放的地址。

// allocating memory to num 
int* num = malloc(8); 
*num = 100; 

// de-allocated the space allocated to num 
free(num); 

// num is already freed there for it cause segmentation fault
*num = 110; 



堆栈溢出-:堆栈内存不足
超出范围访问数组'
使用printf()scanf()时使用错误的格式说明符'


#15 楼

请考虑以下代码片段,
SNIPPET 1
int *number = NULL;
*number = 1;

int *number = malloc(sizeof(int));
*number = 1;

我假设您知道函数的含义:malloc()sizeof()正在问这个问题。
现在解决了,
SNIPPET 1会抛出分段错误错误。
而SNIPPET 2不会。
这就是为什么。
代码段的第一行正在创建一个变量(* number)来存储其他变量的地址,但是在这种情况下,它被初始化为NULL。另一方面,代码段2的第二行正在创建相同的变量(* number)来存储其他变量的地址,在这种情况下,它被赋予一个内存地址(因为malloc()是C / C ++中的一个函数,它返回计算机的内存地址)
要点是,您不能将水倒入未购买的碗或已购买但未经您授权使用的碗中。
当您尝试这样做时,计算机会收到警报并提示您抛出一个SegFault错误。
您只应该在接近底层语言(例如C / C ++)的语言中遇到此错误。其他高级语言中有一种抽象确保您不会发生此错误。
了解细分错误不是特定于语言的也是至关重要的。