Đây là công việc của tôi:
Sự phát triển của ngôn ngữ C cung cấp một số thông tin chi tiết về sự phát triển của kiểu mảng trong C:
Tôi sẽ cố gắng phác thảo mảng:
Tiền thân của C là B và BCPL không có kiểu mảng riêng biệt, một khai báo như:
auto V[10] (B)
or
let V = vec 10 (BCPL)
sẽ khai báo V là một con trỏ (không định kiểu) được khởi tạo để trỏ tới vùng không sử dụng gồm 10 "từ" của bộ nhớ. B đã được sử dụng *
cho hội nghị con trỏ và có []
ký hiệu viết tay ngắn, *(V+i)
có nghĩa là V[i]
, giống như trong C / C ++ ngày nay. Tuy nhiên, V
không phải là một mảng, nó vẫn là một con trỏ phải trỏ đến một số bộ nhớ. Điều này gây ra rắc rối khi Dennis Ritchie cố gắng mở rộng B với các loại cấu trúc. Anh ấy muốn các mảng trở thành một phần của cấu trúc, như trong C ngày nay:
struct {
int inumber;
char name[14];
};
Nhưng với khái niệm B, BCPL về mảng là con trỏ, điều này sẽ yêu cầu name
trường chứa một con trỏ phải được khởi tạo trong thời gian chạy tới vùng nhớ 14 byte trong cấu trúc. Vấn đề khởi tạo / bố trí cuối cùng đã được giải quyết bằng cách cung cấp cho mảng một cách xử lý đặc biệt: Trình biên dịch sẽ theo dõi vị trí của mảng trong cấu trúc, trên ngăn xếp, v.v. mà không thực sự yêu cầu con trỏ tới dữ liệu hiện thực hóa, ngoại trừ trong các biểu thức liên quan đến mảng. Việc xử lý này cho phép hầu hết tất cả mã B vẫn chạy và là nguồn của quy tắc "mảng chuyển đổi thành con trỏ nếu bạn nhìn vào chúng" . Đây là một bản hack khả năng tương thích, hóa ra rất tiện dụng, vì nó cho phép các mảng có kích thước mở, v.v.
Và đây là dự đoán của tôi tại sao không thể gán mảng: Vì mảng là con trỏ trong B, bạn chỉ cần viết:
auto V[10];
V=V+5;
để căn cứ lại một "mảng". Điều này bây giờ vô nghĩa, bởi vì cơ sở của một biến mảng không phải là một giá trị nữa. Vì vậy, việc gán này không được phép, điều này đã giúp bắt được một số chương trình làm điều này phục hồi trên các mảng đã khai báo. Và sau đó khái niệm này bị mắc kẹt: Vì các mảng không bao giờ được thiết kế để trở thành hạng nhất của hệ thống loại C, chúng hầu như được coi như những con thú đặc biệt sẽ trở thành con trỏ nếu bạn sử dụng chúng. Và từ một quan điểm nhất định (bỏ qua rằng mảng C là một sự tấn công giả mạo), việc không cho phép gán mảng vẫn có ý nghĩa: Một mảng mở hoặc một tham số hàm mảng được coi như một con trỏ không có thông tin về kích thước. Trình biên dịch không có thông tin để tạo phép gán mảng cho chúng và việc gán con trỏ là bắt buộc vì lý do tương thích.
typedef int vec[3];
void f(vec a, vec b)
{
vec x,y;
a=b;
x=y;
a=x;
x=a;
}
Điều này không thay đổi khi bản sửa đổi của C vào năm 1978 đã thêm phép gán cấu trúc ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Mặc dù các bản ghi là các kiểu riêng biệt trong C, không thể gán chúng vào đầu K&R C. Bạn phải sao chép chúng thành viên bằng memcpy và bạn chỉ có thể chuyển các con trỏ tới chúng dưới dạng tham số hàm. Phép gán (và truyền tham số) bây giờ được định nghĩa đơn giản là bản ghi nhớ của bộ nhớ thô của cấu trúc và vì điều này không thể phá vỡ mã exsisting nên nó đã được bổ sung một cách dễ dàng. Là một tác dụng phụ không mong muốn, điều này đã ngầm giới thiệu một số kiểu gán mảng, nhưng điều này đã xảy ra ở đâu đó bên trong một cấu trúc, vì vậy điều này thực sự không thể đưa ra các vấn đề với cách sử dụng mảng.