2011-01-06 40 views
5

Tôi đã rất ngạc nhiên khi chương trình sau không gặp sự cố.Truy cập các phần tử kết cấu bằng con trỏ

typedef struct _x { 
    int a; 
    char b; 
    int c; 
} x; 

main() { 
    x *ptr = 0; 
    char *d = &ptr->b; 
} 

Theo hiểu biết của tôi, nhà điều hành -> có quyền ưu tiên cao hơn nhà điều hành &. Vì vậy, tôi dự kiến ​​chương trình sẽ gặp sự cố tại câu lệnh dưới đây khi chúng tôi cố gắng dereference con trỏ NULL tr.

char *d = &ptr->b; 

Nhưng tuyên bố &ptr->b đánh giá địa chỉ hợp lệ. Ai đó có thể vui lòng giải thích nơi tôi sai?

+0

Điều này tương tự như macro 'offsetof'. – ruslik

Trả lời

2

&ptr->b == sizeof(int), có nghĩa là bù đắp của b trong _x sau _x.a (mà là loại int) liên quan đến địa chỉ *((x*)0). Độ lệch của 4 (điển hình cho kiến ​​trúc 32 bit) được lưu trong con trỏ d. Bạn phải truy cập vào d để nhận lỗi seg.

4

Lý do mã của bạn không bị lỗi là bạn không thực sự coi trọng con trỏ. Chú ý rằng các biểu hiện

&ptr->b 

không thực sự cố gắng tải các nội dung của ptr hoặc ptr->b. Thay vào đó, nó chỉ lưu trữ địa chỉ của nơi này nằm trong bộ nhớ. Những gì bạn sẽ nhận được là một con trỏ đến nơi lĩnh vực b của đối tượng được chỉ định bởi ptr nên được. Đây sẽ là một vài byte quá khứ địa chỉ 0, do đó, dereferencing con trỏ bạn vừa tạo ra sẽ gây ra một segfault.

2

Tính toán địa chỉ không yêu cầu truy cập bộ nhớ. &ptr->b có nghĩa là "cung cấp cho tôi địa chỉ của trường b của cấu trúc được trỏ đến bởi ptr". Làm như vậy không yêu cầu xem bất cứ điều gì có thể được lưu trữ trong vị trí bộ nhớ đó.

Có thể hữu ích khi nghĩ đến việc lập chỉ mục một mảng thay vì cấu trúc. C xác định ptr[5] tương đương với *(ptr + 5), có nghĩa là &(ptr[5]) giống với &(*(ptr + 5)). Giờ đây, bạn có thể dễ dàng thấy rằng &* "hủy" và để bạn ở chế độ (ptr + 5), chỉ liên quan đến tăng số lượng con trỏ chứ không phải tải từ bộ nhớ.

C làm cho điều này hơi có mây vì nó phân biệt giá trị từ các giá trị. Đó là, một biểu hiện đề cập đến bộ nhớ được xử lý khác nhau ở phía bên tay trái của một biểu thức hơn là ở bên phải. Với một tuyên bố như x = y;, trình biên dịch C sẽ tải một giá trị từ địa chỉ của y và lưu trữ nó theo địa chỉ x. Đây là sự khác biệt: y hoàn toàn bị hủy đăng ký, nhưng x thì không.

5

Kỳ vọng của bạn không có cơ sở. C chương trình không nhất thiết phải "sụp đổ" khi bạn dereference null con trỏ. Các chương trình C trưng bày như vậy gọi là hành vi không xác định khi bạn cố gắng thực hiện điều gì đó tương tự. Hành vi không xác định có thể tự biểu hiện bằng nhiều cách khác nhau. Nó có thể dẫn đến sự cố. Hoặc nó có thể tạo ra một cái gì đó mà thậm chí giống như một chương trình "làm việc". Sau này là những gì dường như đã xảy ra trong trường hợp của bạn.

Nhưng trong mọi trường hợp, hành vi của chương trình của bạn không xác định.Và không, nó không tạo ra một "địa chỉ hợp lệ" như bạn có vẻ nhầm lẫn. Một địa chỉ số tương ứng với một vị trí trong bộ nhớ mà không tồn tại đối tượng nào không hợp lệ (ngoại trừ giá trị con trỏ null).

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