(Translated by https://www.hiragana.jp/)
記憶體區段錯誤 - 维基百科,自由的百科全书 とべ转到内容ないよう

記憶きおくたいだん錯誤さくご

本页使用了标题或全文手工转换
维基百科ひゃっか自由じゆうてき百科ひゃっかぜん

記憶きおくたいだん錯誤さくご英語えいごSegmentation fault經常けいじょう縮寫しゅくしゃためsegfault),またわけため記憶きおくたいだん错误,也稱そん權限けんげん衝突しょうとつ(access violation),いちしゅほどしき錯誤さくご

它會出現しゅつげんざいとうほどしき企圖きとそんCPU無法むほうじょうてき記憶きおくたいだんときとう錯誤さくご發生はっせいかたからだかい通知つうち作業さぎょう系統けいとうさんせいりょう記憶きおくたいそん權限けんげん衝突しょうとつてき狀況じょうきょう作業さぎょう系統けいとう通常つうじょうかいさんせいかくこころうたてもうか(core dump)以方便びんほどしきいん進行しんこうじょ錯。通常つうじょう錯誤さくごよし于調よういち,而該ためそら(NULL)しょ造成ぞうせいてきれい鏈表ちゅう調ちょうよう一個未分配地址的空鏈錶單元的元素。かず组访问越かい可能かのう产生这个错误。

がいじゅつ[编辑]

とうほどじょ试图访问まこと许访问的ないそん位置いちある试图以不まこと许的方式ほうしき访问ないそん位置いちれい如,尝试うつしにゅうただ位置いちあるくつがえ操作そうさけい统的いち部分ぶぶん)时,かい产生储存だん错误。

术语“分段ぶんだんざい计算中有ちゅうう用途ようと;“储存だん错误”1950年代ねんだい以来いらい就一ちょく使用しようてき术语。[1]とうゆうないそん护时,ただゆうほどじょ自己じこてきそら间是读的,其中ただゆううずたか栈和ほどじょすうすえだんてき读写部分ぶぶんうつしてき,而只读数すえ和代かずよ码段不可ふかうつしてきよし此,尝试读取ほどじょそら间之外的がいてきすうすえあるうつしにゅういたりただ读内そんだん时,かい导致储存だん错误。

ざい使用しようかたけんないそん分段ぶんだんらい提供ていきょうきょ拟内そんてきけい统上,とうかたけん检测到尝试引用いんよう存在そんざいてきだんある引用いんようだんかい限外げんがいてきないそんある引用いんよう无访问权げんてきないそん段中だんなかてきすうすえ时,かい发生储存だん错误。ざい使用しようない存分ぞんぶん页的けい统上,无效ないそん页错误通常会じょうかい导致储存だん错误,而储そんだん错误ないそん页错误都きょ拟内そん管理かんりけい统引发的错误。储存だん错误也可以独立どくりつ于内そん页错误发せいほう访问有效ゆうこうてきないそん页是かい导致储存だん错误,而非无效ないそん页错误。并且だん错误可能かのう发生ざいないそん页中间(いん此没ゆうないそん页错误),れい如处于同一内存页内但非法覆盖内存的缓冲区溢出。

ざいかたけん级别,ざいほう访问时,如果引用いんようてきないそん存在そんざい,错误最初さいしょよしないそん管理かんり单元(MMU)ほうさく为其ないそん护功のうてきいち部分ぶぶんある无效ないそん页错误(如果引用いんようてきないそん存在そんざい)。如果问题无效てき逻辑而是无效てき物理ぶつり,则会引发总线错误,つきかん并不总是能これよし区分くぶん这些错误。

ざい操作そうさけい统级别,这个错误かい获,并传递一个信号给有问题的进程,げきかつ该进ほどてき信号しんごう处理ほどじょ不同ふどうてき操作そうさけい统有不同ふどうてき信号しんごう名称めいしょうらい表示ひょうじ发生りょう储存だん错误。ざい类Unix操作そうさけい统上,一个被称为SIGSEGVてき信号しんごう发送いた该进ほどざいMicrosoft Windowsじょう,该进ほどかいおさむいたSTATUS_ACCESS_VIOLATION异常。

错误原因げんいん[编辑]

储存だん错误产生てき条件じょうけんひょう现方しき决于かたけん操作そうさけい统:不同ふどうてきかたけんかいざい产生いち样的错误,且不同ふどうてき操作そうさけい统会はた这些错误转换なり不同ふどうてき信号しんごう发送给线ほど。 确定储存だん错误てき根本こんぽん原因げんいんざいぼう些情况下じゅうふん容易よういれい如:访问そらゆび针所指向しこうてきないそんそら间),ほどじょかい不断ふだん导致储存だん错误。ざい其他てきいち些情况,储存だん错误可能かのう难以じゅう现或しゃざいりょういたてき时候现,这会让寻找储そんだん错误てき根本こんぽん原因げんいん变得こま难。

以下いか一些导致储存器段错误的一般原因:

  • 试图访问存在そんざいてきないそんそら间(进程ないそんそら间以がい
  • 试图访问ぼつゆう权限てきないそんそら间(れい如:访问操作そうさけい统内かくてきないそん
  • 试图うつしにゅういたりただ读内そんだんれい如:だい码段)

以下いか一些导致储存器段错误的一般编程错误:

  • 引用いんようそらゆび
  • 引用いんようはつはじめ的野まとのゆび
  • 引用いんようやめ经被调用free()函数かんすう释放りょうてき悬空ゆび
  • 缓冲溢出
  • うずたか栈溢
  • 运行せい确编译的ほどじょつきかん存在そんざい编译时错误,ぼう些编译器依然いぜんかい输出行文こうぶんけん

ざいCだい码中,ゆかり容易ようい错误使用しようゆび针,储存だん错误さいつね发生。ゆう其是ざいCてき动态ないそん分配ぶんぱいちゅう。 试图访问そらゆび针所指向しこうてきないそん区域くいき总是かい导致储存だん错误。而野ゆび针和悬空ゆび针则ゆう时会导致储存だん错误,ゆう时则かい。这是いん为野ゆび针和悬空ゆび针所指向しこうてきないそん可能かのう存在そんざい可能かのう存在そんざい可能かのううつしにゅう可能かのう不可ふかうつしいれ。这会导致储存だん错误かい现在りょういたてき时候。

char *p1 = NULL;           // そらゆび
char *p2;                  // ゆび针:はつはじめてきゆび
char *p3  = malloc(10 * sizeof(char));  // 获取动态ないそん并初はじめゆび针(かり设malloc函数かんすうぼつゆう错)
free(p3);                  // p3しょ指向しこうてき动态ないそん释放掉,p3变成悬空ゆび

char c1 = *p1;             // 试图访问そらゆび针所指向しこうてきないそん总是かい导致储存だん错误
char c2 = *p2;             // 试图访问ゆび针所指向しこうてきないそんかい导致ずいつくえすうすえ
char c3 = *p3;             // 试图访问悬空ゆび针所指向しこうてきないそん可能かのうかい导致ずいつくえすうすえ

试图访问这些ゆび针中てきにんなん一个所指向的内存都可能导致分段错误:试图访问そらゆび针通常会じょうかい导致储存だん错误;访问ゆび针所指向しこうてきないそん可能かのうかい导致ずいつくえすうすえいん为指针未はつはじめゆび针所指向しこうてきないそんずいつくえすう;而访问悬そらゆび针所指向しこうてきないそん可能かのうかいざい一定时间内访问到有效数据,ただしとう该数すえ被覆ひふく盖掉きさきかい导致ずいつくえすうすえ

处理储存だん错误[编辑]

储存だん错误ある总线错误てきだま操作そうさ异常终止さわ发该错误てき进程。可能かのうかい生成せいせい核心かくしんぶんけん以帮じょ调试,并且还可能かのう执行赖于其他平台ひらだいてき操作そうされい如,使用しようgrsecurity补丁てきLinuxけい统可能会のうかい记录SIGSEGV信号しんごうとう发生储存だん错误时,Linuxけい统会产生いちSIGSEGV信号しんごう,发生该错误的进程かい获到该信ごう并异つね终止该进ほどあるもの调用该进ほどあずか该信ごう绑定函数かんすう),以便监视可能かのう使用しよう缓冲出来できてきほういれおかせ

ざいぼう些系统上,れい如LinuxWindows,ほどじょ本身ほんみ以处储存だん错误。[2]すえ体系たいけい结构操作そうさけい统的不同ふどうせいざい运行てきほどじょ仅可以处事件じけん,还可以提一些有关其状态的信息,れい如获うずたか栈跟踪、处理よせそん值、さわ发时てきみなもとだい码行、无效访问てきないそん[3]以及该操作そうさ读取还是うつしいれ[4]

つきかん储存だん错误通常つうじょう意味いみほどじょ存在そんざい需要じゅようおさむ复的错误,ただし可能かのう于测试、调试以及需要じゅよう直接ちょくせつ访问ないそんてき平台ひらだいてき目的もくてき故意こい导致此类故障こしょうざいきさきいち种情况下,けい统必须能够允许程じょざい发生故障こしょうきさき继续运行。ざい这种じょう况下,とうけい统允许时,以处事件じけん增加ぞうか处理ほどじょ计数以“とべ过”错误てき指令しれい以继续执ぎょう[5]

れい[编辑]

うつしにゅういたりただ读内そんだん[编辑]

试图うつしにゅういたりただ读内そんだんかい引发储存だん错误。ざいだい码错误的级别,とうほどじょしょうすうすえうつしにゅういたり其代码段てきあるただ读数すえだん时,就会发生储存だん错误。 以下いかいち个ANSI Cだい码示れい,此段だい码通常会じょうかいざい具有ぐゆうないそん护的平台ひらだいじょう导致储存だん错误。它试图修あらためくし文字もじすえANSI C标准,这是未定みてい义的ぎょう为。だい多数たすう编译かいざい编译时捕获它,并将其编译成かいくずし溃的执行だい码:

int main(void){
    const char *s = "hello world\n";
    *s = 'H';
    printf("%s", s);
    return 0;
}

编译包含ほうがん此代このしろ码的ほどじょ时,くし“hello world”放置ほうちざいほどじょ行文こうぶんけんてきrodata部分ぶぶんかずすえだんてきただ读部ぶん载该ほどじょ时,操作そうさけい统将它与其他くしかずつねりょうすうすえ一起放在内存的只读段中。执行时,ゆび针s设置为指向しこうくしてき位置いち,并试图通过该ゆび针将Hうつしにゅういたりただ读内そんだん,从而导致储存だん错误。使用しよう编译时不检查ただ位置いち分配ぶんぱいてき编译らい编译这样てきほどじょ,并在类Unix操作そうさけい统上运行かい产生以下いか运行时错误:

$ gcc segfault.c -g -o segfault
$ ./segfault
储存だん错误

GDB产生てき核心かくしんぶんけん

Program received signal SIGSEGV, Segmentation fault.
0x1c0005c2 in main () at segfault.c:6
6               *s = 'H';

以使用字ようじすう组来代替だいたいゆび针以更正こうせいだい码,いん为该くしかい存在そんざいうずたか栈中,而非ただ读数すえ段中だんなか:

int main(void){
    char s[] = "hello world\n";
    *s = 'H';
    printf("%s", s);
    return 0;
}

编译并运ぎょう以上いじょうだい码:

$ gcc no_segfault.c -g -o no_segfault
$ ./segfault
Hello world

つきかん应该おさむあらためくしちゅうてき文字もじ(这在C标准ちゅう具有ぐゆう未定みてい义的ぎょう为),ただしざいCちゅう它们static char []类型,[6][7][8]いん原始げんしだい码中ぼつゆう隐性转换(指向しこう该数组的ゆび针),而在 C++ ちゅう,它们static const char []类型,存在そんざい隐性转换,いん此编译器通常つうじょうかい获该错误。

试图访问そらゆび针所指向しこうてきないそん[编辑]

ざいC类C语言ちゅうそらゆび针用于表示ひょうじぼつゆう对象てきゆび针”并作为错误指示しじ,而试图读あるうつしにゅうそらゆび针所指向しこうてきないそん是非ぜひ常常つねづね见的ほどじょ错误。C标准并没ゆうゆびあかりそらゆび针与指向しこうないそん0てきゆび针相どうつきかんざい实践ちゅう可能かのう这种じょう况。だい多数たすう操作そうさけい统把そらゆび针映しゃいたりかい产生储存だん错误てきないそん。C标准证此ぎょう为。试图读取あるうつしにゅうそらゆび针所指向しこうてきない存在そんざいC标准ちゅう未定みてい义的ぎょう为。

以下いかしめせれいだい码创けんいち个空ゆび针,しかきさき试图访问其值(读取该值)。运行这段だい码会导致多数たすう操作そうさけい统产せい储存だん错误:

int *p_num = NULL;
printf("%d\n", *p_num);

以下いかしめせれいだい码创けん一个空指针,并试图写いれすうすえ。运行这段だい码会导致储存だん错误:

int *p_num = NULL;
*p_num = 1;

以下いかてきだい包含ほうがん一个空指针的解引用,ただし编译时通常つうじょうかい导致储存だん错误,いん为该值未使用しよういん此该かい引用いんよう通常つうじょうかいとう做死だい码被しょうじょ掉以优化だい码:

int *p_num = NULL;
*p_num;

缓冲溢出[编辑]

うずたか栈溢[编辑]

以下いかだい码是一个没有出口的递归:

int main(void){
    main();
    return 0;
}

这会导致うずたか栈溢,从而导致储存だん错误。[9]すえ编程语言、编译执行てき优化和代かずよ码的确切结构,无限递归一定会导致堆栈溢出。ざい这种じょう况下,无法访问だい码(return 语句)てきぎょう为是未定みてい义的,いん此编译器以消じょ它并且使よう可能かのう导致不用ふよううずたか栈的调优。其他优化可能かのう包括ほうかつはた递归转换为迭だい,鉴于しめせれい函数かんすうてき结构,ほどじょはたえい远运ぎょうどう时大がいりつかい导致うずたか栈溢

参考さんこう资料[编辑]

  1. ^ Debugging Segmentation Faults and Pointer Problems - Cprogramming.com. www.cprogramming.com. [2021-02-03]. (原始げんし内容ないようそん档于2022-07-10). 
  2. ^ Cleanly recovering from Segfaults under Windows and Linux (32-bit, x86). [2020-08-23]. (原始げんし内容ないようそん档于2021-09-13). 
  3. ^ Implementation of the SIGSEGV/SIGABRT handler which prints the debug stack trace.. [2020-08-23]. (原始げんし内容ないようそん档于2021-09-13). 
  4. ^ How to identify read or write operations of page fault when using sigaction handler on SIGSEGV?(LINUX). [2020-08-23]. (原始げんし内容ないようそん档于2021-09-13). 
  5. ^ LINUX – WRITING FAULT HANDLERS. [2020-08-23]. (原始げんし内容ないようそん档于2021-09-13). 
  6. ^ 6.1.4 String literals. ISO/IEC 9899:1990 - Programming languages -- C. 
  7. ^ 6.4.5 String literals. ISO/IEC 9899:1999 - Programming languages -- C. 
  8. ^ 6.4.5 String literals. ISO/IEC 9899:2011 - Programming languages -- C. [2021-09-13]. (原始げんし内容ないようそん档于2022-04-21). 
  9. ^ What is the difference between a segmentation fault and a stack overflow?页面そん档备份そん互联网档あん) at Stack Overflow