2014-12-14 16 views
5

Theo N1570 (C11 dự thảo) 6.5.6/8khai thác Additive:Lý do cơ bản cho một phần tử cuối cùng của một đối tượng mảng là gì?

Hơn nữa, nếu biểu thức P điểm đến yếu tố cuối cùng của một đối tượng mảng , khái niệm (P)+1 điểm một quá khứ yếu tố cuối cùng của đối tượng mảng và nếu biểu thức Q điểm vượt qua phần tử cuối của đối tượng mảng, biểu thức (Q)-1 trỏ đến phần tử cuối cùng của đối tượng mảng

phân lớp 6.5.6/9 cũng chứa:

Hơn nữa, nếu biểu thức P điểm hoặc để một phần tử của một mảng đối tượng hoặc một quá khứ yếu tố cuối cùng của một đối tượng mảng, và các biểu hiện Q điểm đến phần tử cuối cùng của cùng một đối tượng mảng, biểu thức ((Q)+1)-(P) có cùng giá trị với ((Q)-(P))+1 và là -((P)-((Q)+1)) và có giá trị bằng 0 nếu biểu thức P chỉ một phần tử cuối cùng của đối tượng mảng, mặc dù biểu thức (Q)+1 không trỏ đến một phần tử của đối tượng mảng. 106)

này biện minh cho số học con trỏ của như thế này mới có giá trị:

#include <stdio.h> 

int main(void) 
{ 
    int a[3] = {0, 1, 2}; 
    int *P, *Q; 

    P = a + 3; // one past the last element 
    Q = a + 2; // last element 

    printf("%td\n", ((Q)+1)-(P)); 
    printf("%td\n", ((Q)-(P))+1); 
    printf("%td\n", -((P)-((Q)+1))); 

    return 0; 
} 

tôi mong chờ để không cho phép trỏ đến phần tử của mảng out-of-bounds, mà dereference hoạt động hành vi như không xác định (mảng tràn), do đó nó làm cho nó có khả năng nguy hiểm. Có lý do nào cho việc này không?

+1

"Tôi hy vọng sẽ không cho phép trỏ đến phần tử của mảng ngoài giới hạn, mà hành vi dereference đóng vai trò là hành vi không xác định". Dereferencing yếu tố này không được phép nên không có vấn đề gì. "Có lý do nào cho việc này không?" Có, quá nhiều để liệt kê. Google cho * phạm vi nửa mở trong C *. –

+0

C không (bình thường) thực hiện kiểm tra giới hạn, vì vậy mã sẽ biên dịch tốt. Tuy nhiên, việc truy cập bộ nhớ bên ngoài giới hạn của mục đang được truy cập là hành vi không xác định và có thể/sẽ dẫn đến sự kiện lỗi seg. – user3629249

Trả lời

7

Chỉ định phạm vi lặp lại là half-closed interval[start, end), đặc biệt là đối với chỉ mục mảng, có các thuộc tính dễ chịu nhất định như Dijkstra quan sát được trong one of his notes.

1) Bạn có thể tính toán kích thước của dải ô như một hàm đơn giản của end - start. Đặc biệt, nếu phạm vi được xác định theo các chỉ số mảng, số lần lặp được thực hiện bởi vòng lặp sẽ được cho bởi end - start. Nếu phạm vi là [start, end], thì số lần lặp lại sẽ là end - start + 1 - rất khó chịu, phải không? :)

2) quan sát thứ hai Dijsktra của chỉ áp dụng cho các trường hợp (không âm) chỉ số không thể thiếu - xác định một phạm vi như [start, end)(start, end] cả hai đều có tài sản đề cập trong 1). Tuy nhiên, chỉ định nó là (start, end] yêu cầu bạn cho phép chỉ mục -1 đại diện cho phạm vi vòng lặp bao gồm chỉ số 0 - bạn đang cho phép giá trị "không tự nhiên" của -1 chỉ để đại diện cho dải ô. Công ước [start, end) không có vấn đề này, bởi vì end là một số nguyên không âm, và do đó là một lựa chọn tự nhiên khi giao dịch với các chỉ mục mảng.

Phản đối của Dijsktra để cho phép -1 không có điểm tương đồng để cho phép vượt quá địa chỉ hợp lệ cuối cùng của vùng chứa.Tuy nhiên, vì quy ước trên đã được sử dụng quá lâu nên có khả năng đã thuyết phục ủy ban tiêu chuẩn đưa ra ngoại lệ này.

3

Lý do khá đơn giản. Trình biên dịch không được phép đặt một mảng ở cuối bộ nhớ. Để minh họa, giả sử rằng chúng ta có một máy 16 bit với các con trỏ 16 bit. Địa chỉ thấp là 0x0000. Địa chỉ cao là 0xffff. Nếu bạn khai báo char array[256] và trình biên dịch định vị array tại địa chỉ 0xff00, thì về mặt kỹ thuật mảng sẽ khớp với bộ nhớ, sử dụng địa chỉ 0xff00 thông qua 0xffff bao gồm. Tuy nhiên, khái niệm

char *endptr = &array[256]; // endptr points one past the end of the array 

sẽ tương đương với

char *endptr = NULL;   // &array[256] = 0xff00 + 0x0100 = 0x0000 

Có nghĩa là vòng lặp sau đây sẽ không làm việc, kể từ ptr sẽ không bao giờ ít hơn 0

for (char *ptr = array; ptr < endptr; ptr++) 

Vì vậy, các bộ phận bạn trích dẫn chỉ đơn giản là luật sư nói, "Đừng đặt mảng ở cuối vùng nhớ".


lịch sử lưu ý: các bộ xử lý x86 đầu tiên sử dụng một chương trình bộ nhớ phân đoạn trong đó các địa chỉ bộ nhớ nơi quy định bởi một con trỏ thanh ghi 16-bit và một đoạn đăng ký 16-bit. Địa chỉ cuối cùng được tính bằng cách dịch chuyển thanh ghi phân đoạn còn lại 4 bit và thêm vào con trỏ, ví dụ:

pointer register 1234 
segment register AB00 
        ----- 
address in memory AC234 

Không gian địa chỉ kết quả là 1MByte, nhưng có giới hạn bộ nhớ cuối mỗi 64Kbyte. Đó là một lý do để sử dụng luật sư nói thay vì nói, "Đừng đặt mảng vào cuối bộ nhớ" bằng tiếng Anh đơn giản.

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