2010-06-03 62 views
9

Tại sao đệ quy vô hạn dẫn đến lỗi seg? Tại sao ngăn xếp tràn dẫn đến lỗi seg. Tôi đang tìm kiếm giải thích chi tiết.Tại sao đệ quy vô hạn dẫn đến lỗi seg

int f() 
{ 
    f(); 
} 

int main() 
{ 
    f(); 
} 
+0

Xem [Tại sao lỗi phân đoạn đoạn mã C++ này?] (Http://stackoverflow.com/questions/2809014/why-does-this-c-code-snippet-segmentation-fault). –

Trả lời

16

Mỗi khi bạn gọi f(), bạn tăng kích thước của ngăn xếp - đó là nơi địa chỉ trả về được lưu trữ để chương trình biết nơi cần đến khi f() hoàn tất. Khi bạn không bao giờ thoát f(), ngăn xếp sẽ tăng ít nhất một địa chỉ trả về cho mỗi cuộc gọi. Khi phân đoạn ngăn xếp đầy, bạn sẽ gặp lỗi segfault. Bạn sẽ nhận được kết quả tương tự trong mọi hệ điều hành.

+1

Ngoại trừ các trình biên dịch tối ưu hóa các cuộc gọi đuôi thành vòng lặp. Nhưng bạn trả lời là chính xác anyway, 1 từ tôi – qrdl

2

AFAIK: Các đầu của ngăn xếp được bảo vệ bởi các địa chỉ không thể truy cập vào quy trình. Điều này ngăn cản ngăn xếp phát triển trên cấu trúc dữ liệu được phân bổ và hiệu quả hơn việc kiểm tra kích thước ngăn xếp một cách rõ ràng, vì bạn vẫn phải kiểm tra bảo vệ bộ nhớ.

+0

Điều này có ý nghĩa – Pqr

14

Segmentation fault là điều kiện khi chương trình của bạn cố gắng truy cập vào vị trí bộ nhớ mà nó không được phép truy cập. Việc đệ quy vô hạn làm cho chồng của bạn phát triển. Và phát triển. Và phát triển. Cuối cùng nó sẽ phát triển đến một điểm khi nó sẽ tràn vào một khu vực bộ nhớ mà chương trình của bạn bị cấm truy cập bởi hệ điều hành. Đó là khi bạn nhận được lỗi phân đoạn.

+1

Tôi muốn nói đây là câu trả lời hay nhất .. – Jeriko

+0

Tôi đã nghe thuật ngữ "stack-heap collision" được sử dụng để mô tả điều này. – Maxpm

+0

@Maxpm, có ý nghĩa hoàn hảo. Chồng và đống thường mọc về phía nhau. Vì vậy, nếu ngăn xếp phát triển quá lớn, nó có thể tràn vào heap. – Dima

3

Nó vẫn là một stackoverflow ;-)

Cái này là thời gian chạy C không cung cấp "instrumentalisation" như ngôn ngữ quản lý khác làm (ví dụ như Java, Python, vv), vì vậy văn bản bên ngoài không gian thiết kế cho ngăn xếp thay vì gây ra ngoại lệ chi tiết, chỉ tăng lỗi cấp thấp hơn, có tên chung là "lỗi phân đoạn".

Đây là lý do hiệu suất, vì các cơ quan giám sát truy cập bộ nhớ này có thể được thiết lập với sự trợ giúp của hỗ trợ phần cứng với chi phí thấp hoặc không có chi phí; Tôi không thể nhớ chính xác các chi tiết ngay bây giờ, nhưng nó thường được thực hiện bằng cách đánh dấu các bảng trang MMU hoặc với các thanh ghi phân đoạn chủ yếu là lỗi thời.

0

Về bản chất, nguyên tắc này giống như tràn bộ đệm; hệ điều hành phân bổ một lượng bộ nhớ cố định cho ngăn xếp, và khi bạn chạy ra ngoài (ngăn xếp tràn) bạn nhận được hành vi không xác định, mà trong ngữ cảnh này có nghĩa là một SIGSEGV.

Ý tưởng cơ bản:

int stack[A_LOT]; 
int rsp=0; 

void call(Func_p fn) 
    { 
    stack[rsp++] = rip; 
    rip = fn; 
    } 

void retn() 
    { 
    rip = stack[--rsp]; 
    } 

/*recurse*/ 
for(;;){call(somefunc);} 

cuối cùng RSP di chuyển qua phần cuối của chồng và bạn cố gắng để đưa các địa chỉ trả lại tiếp theo trong lưu trữ chưa phân bổ và barfs chương trình của bạn. Rõ ràng các hệ thống thực là một số nhiều hơn phức tạp hơn, nhưng điều đó có thể (và có) chiếm nhiều cuốn sách lớn.

0

Ở mức "thấp", ngăn xếp được "duy trì" thông qua con trỏ (con trỏ ngăn xếp), được giữ trong thanh ghi bộ xử lý. Thanh ghi này chỉ vào bộ nhớ, vì sau đó ngăn xếp là bộ nhớ. Khi bạn đẩy các giá trị trên ngăn xếp, "giá trị" của nó bị giảm dần (con trỏ ngăn xếp di chuyển từ địa chỉ cao hơn đến địa chỉ thấp hơn). Mỗi lần bạn nhập một hàm, một số khoảng trống được "lấy" từ ngăn xếp (các biến cục bộ); hơn nữa, trên nhiều kiến ​​trúc, lời gọi tới chương trình con đẩy giá trị trả về trên stack (và nếu bộ xử lý không có con trỏ ngăn xếp đăng ký đặc biệt, có thể đăng ký "bình thường" cho mục đích này, vì ngăn xếp có ích ngay cả khi các chương trình con có thể được gọi với các cơ chế khác), sao cho ngăn xếp ít nhất bị giảm đi bởi kích thước của một con trỏ (ví dụ, 4 hoặc 8 byte).

Trong vòng lặp đệ quy vô hạn, trong trường hợp tốt nhất chỉ giá trị trả lại làm cho ngăn xếp bị giảm đi ... cho đến khi nó trỏ tới bộ nhớ không thể truy cập được bởi chương trình.Và bạn thấy vấn đề lỗi phân đoạn.

Bạn có thể thấy thú vị this page.

+0

một số kiến ​​trúc RISC lưu trữ địa chỉ trả về trong sổ đăng ký ... nhưng điều này không cho phép đệ quy, cũng không gọi các chương trình con khác, trừ khi nó cho phép sử dụng các thanh ghi khác nhau; vì các thanh ghi thường bị giới hạn, ở cuối sẽ sử dụng những gì thuận tiện hơn để cho phép đệ quy ... tức là ngăn xếp ... hoặc một số cơ chế khác (như MMIX) tuy nhiên tiêu thụ bộ nhớ, có thể được giải quyết một cách khác nhau, và vì vậy cùng một vấn đề nảy sinh sớm hay muộn. – ShinTakezou

1

Một con trỏ chương trình hoặc con trỏ chương trình là một thanh ghi có chứa giá trị của lệnh tiếp theo sẽ được thực thi. Trong một cuộc gọi hàm, giá trị hiện tại của bộ đếm chương trình được đẩy vào ngăn xếp và sau đó là điểm truy cập chương trình để hướng dẫn đầu tiên của hàm. Giá trị cũ được bật lên sau khi trở về từ hàm đó và được gán cho bộ đếm chương trình. Trong đệ quy vô hạn giá trị được đẩy một lần nữa và một lần nữa và dẫn đến tràn ngăn xếp.

4

Tài nguyên hệ thống của bạn là hữu hạn. Chúng bị hạn chế. Ngay cả khi hệ thống của bạn có bộ nhớ và lưu trữ nhiều nhất trên toàn bộ Trái đất, thì vô hạn là WAY LỚN hơn những gì bạn có. Hãy nhớ rằng bây giờ.

Cách duy nhất để làm điều gì đó "vô số lần" là "quên" thông tin trước đó. Đó là, bạn phải "quên" những gì đã được thực hiện trước đây. Nếu không, bạn phải nhớ những gì đã xảy ra trước đó và sẽ lưu trữ một dạng khác (bộ nhớ đệm, bộ nhớ, dung lượng đĩa, viết những thứ trên giấy, ...) - điều này là không thể tránh khỏi. Nếu bạn đang lưu trữ mọi thứ, bạn có một số lượng hữu hạn của không gian có sẵn. Nhớ lại, rằng vô hạn là WAY LỚN hơn những gì bạn có. Nếu bạn cố gắng lưu trữ một lượng thông tin vô hạn, bạn S run hết dung lượng lưu trữ.

Khi bạn sử dụng đệ quy, bạn đang lưu trữ ngầm thông tin trước đó với mỗi cuộc gọi đệ quy. Vì vậy, tại một số điểm bạn sẽ xả dung lượng lưu trữ của bạn nếu bạn cố gắng làm điều này một số lượng vô hạn của mất. Dung lượng lưu trữ của bạn trong trường hợp này là ngăn xếp. Ngăn xếp là một phần của bộ nhớ hữu hạn. Khi bạn sử dụng tất cả và cố gắng truy cập vượt quá những gì bạn có, hệ thống sẽ tạo ra một ngoại lệ mà cuối cùng có thể dẫn đến một lỗi seg nếu bộ nhớ nó cố gắng truy cập được bảo vệ chống ghi. Nếu nó không được bảo vệ bằng văn bản, nó sẽ tiếp tục, ghi đè lên những gì mà người ta biết đến cho đến khi nó cố ghi vào bộ nhớ mà không tồn tại, hoặc nó cố gắng viết cho một số phần khác của bộ nhớ được bảo vệ ghi hoặc cho đến khi nó làm hỏng mã của bạn (trong bộ nhớ).

Các vấn đề liên quan