Một lỗi phân khúc là gì?


598

Một lỗi phân khúc là gì? Có khác nhau ở C và C ++ không? Làm thế nào là lỗi phân khúc và con trỏ lơ lửng liên quan?


94

22
Nếu đó là trường hợp, tại sao trong trường hợp của tôi, trình biên dịch không phàn nàn gì, tất cả đều hoạt động trơn tru, nhưng tại thời điểm chạy, hệ thống sẽ ném một lỗi phân đoạn (kết xuất lõi)? T_T
Jim Raynor

3
Chỉ là một bãi rác bộ nhớ khi có sự cố!
quả

7
@pinouchon: Hài hước, nhưng khi nào trình biên dịch có liên quan đến lỗi seg? Không phải là môi trường thời gian chạy nhiều hơn sao?
dhein

1
Thường được gọi bằng cách cố gắng hủy đăng ký một con trỏ null, do đó, một lỗi phân đoạn thường tương tự như Java NullPointerException.
Raedwald

Câu trả lời:


673

Lỗi phân đoạn là một loại lỗi cụ thể gây ra bởi việc truy cập bộ nhớ mà không thuộc về bạn. Đây là một cơ chế trợ giúp giúp bạn không làm hỏng bộ nhớ và đưa ra các lỗi bộ nhớ khó gỡ lỗi. Bất cứ khi nào bạn gặp lỗi segfault, bạn biết rằng mình đang làm gì đó với bộ nhớ - truy cập biến đã được giải phóng, ghi vào phần chỉ đọc của bộ nhớ, v.v. quản lý bộ nhớ, không có sự khác biệt chính giữa segfaults trong C và C ++.

Có nhiều cách để có được một segfault, ít nhất là trong các ngôn ngữ cấp thấp hơn như C (++). Một cách phổ biến để có được một segfault là hủy bỏ con trỏ null:

int *p = NULL;
*p = 1;

Một segfault khác xảy ra khi bạn cố ghi vào một phần bộ nhớ được đánh dấu là chỉ đọc:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

Con trỏ lơ lửng chỉ vào một thứ không còn tồn tại nữa, như ở đây:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

Con trỏ ptreo lủng lẳng vì nó trỏ đến biến ký tự ckhông còn tồn tại sau khi khối kết thúc. Và khi bạn cố gắng hủy bỏ con trỏ lơ lửng (như *p='A'), bạn có thể sẽ nhận được một segfault.


154
Ví dụ cuối cùng đặc biệt khó chịu, khi tôi xây dựng: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); trả về 0; } Với gcc hoặc một số trình biên dịch khác, nó 'xuất hiện' để hoạt động. Không có cảnh báo về biên dịch. Không có segfault. Điều này là do '}' nằm ngoài phạm vi, không thực sự xóa dữ liệu, chỉ đánh dấu nó là miễn phí để được sử dụng lại. Mã có thể chạy tốt trên một hệ thống sản xuất trong nhiều năm, bạn thay đổi một phần khác của mã, thay đổi trình biên dịch hoặc một cái gì đó khác và BOOOOOM!
Chris Huang-Leaver

36
Xin lỗi vì vết sưng nhưng chỉ là một ghi chú bên lề ... không có ví dụ nào của bạn nhất thiết gây ra lỗi, thực tế đó chỉ là hành vi không xác định ;-)
oldrinb

18
@oldrinb: Không thể viết mã mà nhất thiết phải gây ra một segfault. Ít nhất là vì có những hệ thống hoạt động mà không có bảo vệ bộ nhớ, do đó không thể biết liệu một phần bộ nhớ có thực sự "thuộc về bạn" hay không, và do đó không biết segfaults, chỉ có hành vi không xác định ... (ví dụ AmigaOS cổ điển)
DevSolar

7
@ ChrisHuang-Leaver, bạn cần hiểu đó clà địa phương, điều đó có nghĩa là nó đã được đẩy lên ngăn xếp sau đó {và bật ra khỏi nó sau đó }. con trỏ lơ lửng chỉ là một tham chiếu đến phần bù hiện nằm ngoài ngăn xếp. đó là lý do tại sao sửa đổi nó trong một chương trình đơn giản sẽ không bao giờ kích hoạt bất kỳ segfault nào. mặt khác, nó có thể dẫn đến segfault trong trường hợp sử dụng phức tạp hơn, trong đó các lệnh gọi hàm khác có thể dẫn ngăn xếp phát triển và chứa dữ liệu được trỏ bởi con trỏ lơ lửng. ghi vào dữ liệu đó (vars cục bộ) sẽ dẫn đến hành vi không xác định (segfault & Co)
Ayman Khamouma

3
@ ChrisHuang-Leaver, thông thường khi bạn thoát khỏi phạm vi, trình biên dịch phải khôi phục một số không gian ngăn xếp để giải phóng không gian ngăn xếp không sử dụng, nhưng điều này không xảy ra luôn (với gcc là một trong những trình biên dịch này). Ngoài ra, không gian ngăn xếp được phân bổ thường được sử dụng lại một lần nữa, vì vậy tôi đã nghe nói rằng không có hệ điều hành nào trả lại các trang ngăn xếp không sử dụng cho hệ thống, làm cho chủ đề không gian đó trở thành một SIGSEGV, vì vậy tôi sẽ không mong đợi tín hiệu như vậy từ việc xáo trộn với ngăn xếp.
Luis Colorado

111

Điều đáng chú ý là lỗi phân đoạn không phải do truy cập trực tiếp vào bộ nhớ tiến trình khác (đôi khi đây là điều tôi nghe thấy), vì đơn giản là không thể. Với bộ nhớ ảo, mọi tiến trình đều có không gian địa chỉ ảo riêng và không có cách nào để truy cập vào một bộ nhớ khác bằng bất kỳ giá trị nào của con trỏ. Ngoại lệ cho điều này có thể là các thư viện chia sẻ có cùng không gian địa chỉ vật lý được ánh xạ tới (có thể) các địa chỉ ảo và bộ nhớ kernel khác nhau, thậm chí được ánh xạ theo cùng một cách trong mọi quy trình (để tránh TLB tuôn ra trên tòa nhà chọc trời, tôi nghĩ vậy). Và những thứ như shmat;) - đây là những gì tôi được coi là truy cập 'gián tiếp'. Tuy nhiên, người ta có thể kiểm tra xem chúng thường nằm cách mã quá trình và chúng ta thường có thể truy cập chúng (đây là lý do tại sao chúng ở đó,

Tuy nhiên, lỗi phân đoạn có thể xảy ra trong trường hợp truy cập bộ nhớ (quy trình) của chúng ta theo cách không phù hợp (ví dụ: cố gắng ghi vào không gian không thể ghi). Nhưng lý do phổ biến nhất cho nó là quyền truy cập vào một phần của không gian địa chỉ ảo hoàn toàn không được ánh xạ đến vật lý.

Và tất cả điều này liên quan đến hệ thống bộ nhớ ảo.


Với các tệp được ánh xạ bộ nhớ / bộ nhớ được chia sẻ, người khác có thể gây rối với bộ nhớ của bạn. Trong WIN32 cũng có các API khó chịu như 'WriteProcessMemory'!
paulm

1
@paulm: Vâng, tôi biết. Đây là những gì tôi đã nghĩ trong "Và những thứ như shmat;) - đây là những gì tôi được coi là truy cập 'gián tiếp'."
konrad.kruczynski

Trong một hệ điều hành bộ nhớ ảo, không có cách nào (thông thường, vì vậy, xin vui lòng, những người triển khai hệ điều hành, đừng kích thích tôi vì điều này) để một quá trình truy cập bộ nhớ ảo quá trình khác, không phải là một loại cuộc gọi hệ thống gắn bộ nhớ cho phép bạn truy cập. Địa chỉ bộ nhớ ảo thường có nghĩa là những thứ khác nhau tùy thuộc vào quá trình được xem xét.
Luis Colorado

38

Lỗi phân đoạn là do yêu cầu đối với một trang mà quy trình không được liệt kê trong bảng mô tả hoặc yêu cầu không hợp lệ đối với trang mà nó đã liệt kê (ví dụ: yêu cầu ghi trên trang chỉ đọc).

Một con trỏ lơ lửng là một con trỏ có thể hoặc không thể trỏ đến một trang hợp lệ, nhưng lại trỏ đến một đoạn bộ nhớ "không mong muốn".


10
Điều này là đúng, nhưng nó có thực sự giúp ích cho bạn nếu bạn chưa biết lỗi phân khúc là gì không?
zoul

29

Thành thật mà nói, như các áp phích khác đã đề cập, Wikipedia có một bài viết rất hay về điều này vì vậy hãy xem ở đó. Loại lỗi này rất phổ biến và thường được gọi là những thứ khác như Vi phạm truy cập hoặc Lỗi bảo vệ chung.

Chúng không khác nhau về C, C ++ hoặc bất kỳ ngôn ngữ nào khác cho phép con trỏ. Những loại lỗi này thường được gây ra bởi con trỏ

  1. Được sử dụng trước khi được khởi tạo đúng cách
  2. Được sử dụng sau bộ nhớ mà chúng trỏ đến đã được phân bổ lại hoặc bị xóa.
  3. Được sử dụng trong một mảng được lập chỉ mục trong đó chỉ mục nằm ngoài giới hạn mảng. Điều này thường chỉ khi bạn thực hiện toán con trỏ trên các mảng hoặc chuỗi c truyền thống, chứ không phải các bộ sưu tập dựa trên STL / Boost (trong C ++.)

16

Theo wikipedia:

Lỗi phân đoạn xảy ra khi chương trình cố gắng truy cập vị trí bộ nhớ mà nó không được phép truy cập hoặc cố gắng truy cập vị trí bộ nhớ theo cách không được phép (ví dụ: cố ghi vào vị trí chỉ đọc hoặc để ghi đè lên một phần của hệ điều hành).


13

Lỗi phân đoạn cũng là do lỗi phần cứng, trong trường hợp này là bộ nhớ RAM. Đây là nguyên nhân ít phổ biến hơn, nhưng nếu bạn không tìm thấy lỗi trong mã của mình, có thể một memtest có thể giúp bạn.

Giải pháp trong trường hợp này, thay đổi RAM.

biên tập:

Ở đây có một tài liệu tham khảo: Lỗi phân đoạn theo phần cứng


3
Một thử nghiệm nhanh và bẩn cho RAM bị lỗi là chạy chương trình bị hỏng của bạn nhiều lần trong một vòng lặp. Nếu chương trình không có điều kiện không xác định nội bộ, nghĩa là, nó luôn tạo ra cùng một đầu ra cho cùng một đầu vào, hoặc ít nhất là nó được cho là đối với, nhưng đối với một số đầu vào cụ thể, đôi khi nó bị hỏng , nhưng không phải lúc nào cũng vậy bắt đầu lo lắng về RAM xấu.
zwol

8

Lỗi phân đoạn xảy ra khi một quá trình (phiên bản đang chạy của chương trình) đang cố truy cập địa chỉ bộ nhớ chỉ đọc hoặc phạm vi bộ nhớ đang được quá trình khác sử dụng hoặc truy cập địa chỉ bộ nhớ không tồn tại (không hợp lệ). Vấn đề Tham chiếu (con trỏ) có nghĩa là cố gắng truy cập một đối tượng hoặc biến có nội dung đã bị xóa khỏi bộ nhớ, ví dụ:

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

4
Cách chính xác để xóa một mảng là xóa [] mảng;
Damian

8

Trang Segmented_fault của Wikipedia có một mô tả rất hay về nó, chỉ nêu ra nguyên nhân và lý do. Có một cái nhìn vào wiki để mô tả chi tiết.

Trong điện toán, lỗi phân đoạn (thường được rút ngắn thành segfault) hoặc vi phạm truy cập là lỗi do phần cứng có bảo vệ bộ nhớ, thông báo cho hệ điều hành (HĐH) về vi phạm truy cập bộ nhớ.

Sau đây là một số nguyên nhân điển hình của lỗi phân đoạn:

  • Dereferences NULL con trỏ - điều này được đặc biệt bởi phần cứng quản lý bộ nhớ
  • Cố gắng truy cập địa chỉ bộ nhớ không tồn tại (bên ngoài không gian địa chỉ của quá trình)
  • Cố gắng truy cập bộ nhớ chương trình không có quyền (như cấu trúc kernel trong ngữ cảnh quy trình)
  • Cố gắng ghi bộ nhớ chỉ đọc (chẳng hạn như đoạn mã)

Đến lượt chúng thường do lỗi lập trình dẫn đến truy cập bộ nhớ không hợp lệ:

  • Hủy bỏ hội nghị hoặc gán cho một con trỏ chưa được khởi tạo (con trỏ hoang dã, trỏ đến một địa chỉ bộ nhớ ngẫu nhiên)

  • Hủy bỏ hội nghị hoặc gán cho một con trỏ được giải phóng (con trỏ lơ lửng, trỏ đến bộ nhớ đã được giải phóng / giải phóng / xóa)

  • Một tràn bộ đệm.

  • Một chồng tràn.

  • Cố gắng thực hiện một chương trình không biên dịch chính xác. (Một số trình biên dịch sẽ xuất ra một tệp thực thi mặc dù có lỗi thời gian biên dịch.)


6

Nói một cách đơn giản: lỗi phân đoạn là hệ điều hành gửi tín hiệu đến chương trình nói rằng nó đã phát hiện truy cập bộ nhớ bất hợp pháp và chấm dứt sớm chương trình để ngăn bộ nhớ bị hỏng.


3

"Lỗi phân đoạn" có nghĩa là bạn đã cố truy cập vào bộ nhớ mà bạn không có quyền truy cập.

Vấn đề đầu tiên là với các đối số của bạn là chính. Hàm chính phải là int main(int argc, char *argv[])và bạn nên kiểm tra xem argc có ít nhất 2 trước khi truy cập argv [1] không.

Ngoài ra, vì bạn đang chuyển từ float sang printf (nhân tiện, được chuyển đổi thành gấp đôi khi chuyển sang printf), bạn nên sử dụng công cụ xác định định dạng% f. Trình xác định định dạng% s dành cho các chuỗi (mảng ký tự kết thúc '\ 0').


2

Lỗi phân đoạn hoặc vi phạm truy cập xảy ra khi chương trình cố gắng truy cập vị trí bộ nhớ không tồn tại hoặc cố gắng truy cập vị trí bộ nhớ theo cách không được phép.

 /* "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;

Ở đây tôi [1000] không tồn tại, vì vậy segfault xảy ra.

Nguyên nhân của lỗi phân khúc:

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 processs 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).

2
Trước hết, lỗi seg không liên quan gì đến địa chỉ có hoặc không tồn tại. Đó là về bạn đang truy cập nó, nơi bạn không được phép làm như vậy. Và trong ví dụ đặc biệt của bạn, nó thậm chí còn được đảm bảo theo tiêu chuẩn rằng vị trí đó tồn tại. vì tiêu chuẩn nói trong trường hợp mảng, phải có một địa chỉ hợp lệ cho một con trỏ trỏ trên một mảng được căn chỉnh tốt trong giới hạn của nó VÀ 1 phía sau .
dhein 8/12/2015

nó cũng được liên kết với địa chỉ, nếu bạn không có địa chỉ và nếu bạn cố gắng truy cập địa chỉ này, thì cũng có seg. lỗi. Và trong ví dụ của tôi, nó chỉ để hiểu quan điểm.
Mohit Rohilla

2

Có rất nhiều lời giải thích tốt về "Phân đoạn lỗi" trong các câu trả lời, nhưng kể từ khi có lỗi segmentation thường có một bãi chứa nội dung bộ nhớ, tôi muốn chia sẻ nơi mối quan hệ giữa "cốt lõi đổ" gia lỗi Segmentation (core dumped) và bộ nhớ đến từ:

Từ khoảng năm 1955 đến 1975 - trước khi có bộ nhớ bán dẫn - công nghệ vượt trội trong bộ nhớ máy tính đã sử dụng những chiếc bánh rán từ tính nhỏ được xâu chuỗi trên dây đồng. Các bánh rán được gọi là "lõi ferrite" và bộ nhớ chính được gọi là "bộ nhớ lõi" hoặc "lõi".

Lấy từ đây .


2

Có đủ định nghĩa về lỗi phân đoạn, tôi muốn trích dẫn một vài ví dụ mà tôi đã gặp trong khi lập trình, có vẻ như là những lỗi ngớ ngẩn, nhưng sẽ lãng phí rất nhiều thời gian.

  1. bạn có thể gặp lỗi phân đoạn trong trường hợp dưới đây trong khi kiểu argumet không khớp trong printf

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

đầu ra: Segmentation Fault (SIGSEGV)

  1. khi bạn quên phân bổ bộ nhớ cho một con trỏ, nhưng cố gắng sử dụng nó.

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

đầu ra: Segmentation Fault (SIGSEGV)


1

Ý nghĩa đơn giản Segmentation faultlà bạn đang cố truy cập một số bộ nhớ không thuộc về bạn. Segmentation faultxảy ra khi chúng ta cố gắng đọc và / hoặc ghi các tác vụ trong một vị trí bộ nhớ chỉ đọc hoặc cố gắng giải phóng bộ nhớ. Nói cách khác, chúng ta có thể giải thích điều này như một loại tham nhũng bộ nhớ.

Dưới đây tôi đề cập đến những lỗi phổ biến được thực hiện bởi các lập trình viên dẫn đến Segmentation fault.

  • Sử dụng scanf()sai cách (quên đặt &).
int num;
scanf("%d", num);// must use &num instead of num
  • Sử dụng con trỏ sai cách.
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.
  • Sửa đổi một chuỗi ký tự (con trỏ cố gắng ghi hoặc sửa đổi bộ nhớ chỉ đọc.)
char *str;  

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

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Cố gắng tiếp cận thông qua một địa chỉ đã được giải phóng.
// 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; 
  • Stack Overflow -: Hết bộ nhớ trên stack
  • Truy cập một mảng ngoài giới hạn '
  • Sử dụng chỉ định định dạng sai khi sử dụng printf()scanf()'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.