2010-07-13 40 views
6

Tôi đang cố gắng hiểu cách con trỏ trỏ tới các đối tượng được phân bổ tĩnh làm việc như thế nào và chúng có thể đi sai ở đâu.Con trỏ tới các đối tượng được phân bổ tĩnh

tôi đã viết mã này:

int* pinf = NULL; 
for (int i = 0; i<1;i++) { 
    int inf = 4; 
    pinf = &inf; 
} 

cout<<"inf"<< (*pinf)<<endl; 

Tôi ngạc nhiên rằng nó làm việc vì tôi nghĩ rằng inf sẽ biến mất khi chương trình rời khỏi khối và con trỏ sẽ trỏ đến một cái gì đó không còn tồn tại. Tôi mong đợi một lỗi phân đoạn khi cố truy cập pinf. Ở giai đoạn nào trong chương trình, inf sẽ chết?

+6

Không có đối tượng được phân bổ tĩnh trong mã của bạn. –

+2

... ngoại trừ cout, tất nhiên :-) –

Trả lời

7

Hiểu biết của bạn là chính xác. inf biến mất khi bạn rời khỏi phạm vi vòng lặp và do đó truy cập *pinf mang lại hành vi không xác định. Hành vi không xác định có nghĩa là trình biên dịch và/hoặc chương trình có thể làm bất cứ điều gì, mà có thể là để sụp đổ, hoặc trong trường hợp này có thể chỉ đơn giản là chug cùng.

Điều này là do inf đang ở trên ngăn xếp. Ngay cả khi nó nằm ngoài phạm vi pinf vẫn trỏ đến vị trí bộ nhớ có thể sử dụng trên ngăn xếp. Theo như thời gian chạy là có liên quan địa chỉ ngăn xếp là tốt, và trình biên dịch không bận tâm để chèn mã để xác minh rằng bạn không truy cập vào các địa điểm ngoài kết thúc của ngăn xếp. Điều đó sẽ cực kỳ tốn kém trong một ngôn ngữ được thiết kế cho tốc độ.

Vì lý do này, bạn phải rất cẩn thận để tránh hành vi không xác định. C và C++ không phải là tốt đẹp cách Java hoặc C# là nơi hoạt động bất hợp pháp khá nhiều luôn luôn tạo ra một ngoại lệ ngay lập tức và sụp đổ chương trình của bạn. Bạn lập trình phải thận trọng vì trình biên dịch sẽ bỏ lỡ tất cả các loại lỗi cơ bản mà bạn tạo ra.

+0

+1 cho thông tin (kỹ thuật chính xác) :) –

+0

tốt, địa chỉ vẫn hợp lệ, vì vậy nó chắc chắn sẽ không sụp đổ, và điều này thậm chí còn tồi tệ hơn. – ruslik

2

Nó có thể sẽ không bao giờ chết vì pinf sẽ trỏ đến một cái gì đó trên ngăn xếp.

Ngăn xếp thường không co lại.

Sửa đổi và bạn sẽ được đảm bảo khá nhiều khi ghi đè.

1

Bạn sẽ không nhất thiết phải nhận được SIGSEGV (lỗi phân đoạn). Bộ nhớ inf có thể được cấp phát trong ngăn xếp. Và vùng bộ nhớ ngăn xếp có lẽ vẫn được phân bổ cho quá trình của bạn tại thời điểm đó, vì vậy, đó có thể là lý do tại sao bạn không nhận được lỗi seg.

+0

Đặc biệt không phải nếu bạn chạy trên Windows, nơi bạn sẽ bị vi phạm quyền truy cập thay vì lỗi phân đoạn. – Puppy

+0

@DeadMG: hehehehe ... Đúng vậy. –

0

Lỗi bảo vệ xảy ra khi trang bộ nhớ bạn trỏ đến không còn hợp lệ nữa cho quy trình.

May mắn là hầu hết các hệ điều hành không tạo ra một trang riêng biệt cho mỗi không gian ngăn xếp của mỗi số nguyên.

+0

Hoặc không may, vì nó cho phép các lỗi tiếp tục âm thầm trên đường và xuất hiện muộn hơn với những hậu quả khủng khiếp. Có hàng rào điện (efence) sẽ buộc phân bổ đống vào khối riêng của chúng và giúp bắt các lỗi này, nhưng tôi không nghĩ có bất kỳ giải pháp nào như vậy (hoặc thực sự, có thể là một) cho các biến stack. –

1

Hành vi không xác định, nhưng trong thực tế, "phá hoại" int là một nút, vì vậy hầu hết các trình biên dịch sẽ để số đó trên ngăn xếp cho đến khi có thứ gì đó khác đi kèm để sử dụng lại vị trí cụ thể đó.

Một số trình biên dịch có thể đặt int thành 0xDEADBEEF (hoặc một số rác như vậy) khi nó nằm ngoài phạm vi trong chế độ gỡ lỗi, nhưng điều đó sẽ không làm cho lỗi cout << ... bị lỗi; nó sẽ đơn giản in giá trị vô nghĩa.

3

Bạn sử dụng cái gọi là Dangling pointer. Nó sẽ dẫn đến hành vi không xác định bằng tiêu chuẩn C++.

2

Nếu bạn đang yêu cầu về vấn đề này:

int main() { 
    int* pinf = NULL; 
    for (int i = 0; i<1;i++){ 
    int inf = 4; 
    pinf = &inf; 
    } 
    cout<<"inf"<< (*pinf)<<endl; 
} 

Sau đó, những gì bạn có là hành vi không xác định. Tự động phân bổ (không tĩnh) đối tượng inf đã đi ra khỏi phạm vi và vô lý bị phá hủy khi bạn truy cập nó thông qua con trỏ. Trong trường hợp này, bất cứ điều gì có thể xảy ra, bao gồm cả nó xuất hiện để "làm việc".

+0

+1 để viết Hành vi không xác định. –

1

Bộ nhớ có thể hoặc không thể chứa 4 khi nó đến dòng cout của bạn. Nó có thể chứa một 4 nghiêm túc do tai nạn. :)

Điều đầu tiên trước tiên: hệ điều hành của bạn chỉ có thể phát hiện truy cập bộ nhớ bị lạc lối trên ranh giới trang. Vì vậy, nếu bạn đang đi bằng 4k hoặc 8k hoặc 16k hoặc nhiều hơn. (Kiểm tra /proc/self/maps trên một hệ thống Linux một ngày nào đó để xem bố trí bộ nhớ của một quy trình; bất kỳ địa chỉ nào trong phạm vi được liệt kê đều được phép, bất kỳ bên ngoài phạm vi được liệt kê đều không được phép. Vì vậy, hệ điều hành không thể giúp bạn khi dữ liệu của bạn quá nhỏ.

Ngoài ra, int inf = 4; của bạn cũng có thể được lưu trữ trong các phân đoạn .rodata, .data hoặc .text của chương trình của bạn. Các biến tĩnh có thể được nhồi vào bất kỳ phần nào trong số các phần này (tôi không biết làm thế nào trình biên dịch/trình liên kết quyết định; tôi coi nó là ma thuật) và do đó chúng sẽ hợp lệ trong suốt toàn bộ thời gian của chương trình. Kiểm tra size /bin/sh lần sau khi bạn sử dụng hệ thống Unix để biết có bao nhiêu dữ liệu được đưa vào phần nào. (Và kiểm tra readelf(1) cho cách quá nhiều thông tin. objdump(1) nếu bạn đang ở trên hệ thống cũ.)

Nếu bạn thay đổi inf = 4 để inf = i, sau đó lưu trữ sẽ được cấp phát trên stack, và bạn đứng một cơ hội tốt hơn để nó bị ghi đè nhanh chóng.

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