Tại sao việc lập chỉ mục trong một mảng bắt đầu bằng 0 trong C mà không phải bằng 1?
Tại sao việc lập chỉ mục trong một mảng bắt đầu bằng 0 trong C mà không phải bằng 1?
Câu trả lời:
Trong C, tên của một mảng về cơ bản là một con trỏ [nhưng xem các bình luận] , một tham chiếu đến một vị trí bộ nhớ và do đó biểu thứcarray[n]
đề cập đến một n
thành phần vị trí bộ nhớ cách xa phần tử bắt đầu. Điều này có nghĩa là chỉ số được sử dụng như một phần bù. Phần tử đầu tiên của mảng được chứa chính xác trong vị trí bộ nhớ mà mảng tham chiếu (cách 0 phần tử), vì vậy nó nên được ký hiệu là array[0]
.
Để biết thêm thông tin:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
sizeof arr
mang lại kích thước của đối tượng mảng, không phải kích thước của một con trỏ.
sizeof
toán tử hoặc toán tử đơn nguyên &
hoặc là một chuỗi ký tự được sử dụng để khởi tạo một mảng, một biểu thức có kiểu" mảng thuộc loại "được chuyển đổi thành biểu thức có loại" con trỏ để nhập "trỏ đến phần tử ban đầu của đối tượng mảng và không phải là giá trị. Nếu đối tượng mảng có lớp lưu trữ đăng ký, hành vi không được xác định. "
Câu hỏi này đã được đăng hơn một năm trước, nhưng ở đây ...
Mặc dù bài viết của Dijkstra (trước đây được tham chiếu trong câu trả lời đã bị xóa ) có ý nghĩa từ góc độ toán học, nó không liên quan khi lập trình.
Quyết định được đưa ra bởi đặc tả ngôn ngữ & trình thiết kế trình biên dịch dựa trên quyết định của các nhà thiết kế hệ thống máy tính để bắt đầu đếm từ 0.
Trích dẫn từ một lời cầu xin hòa bình của Daniel Cohen.
Đối với bất kỳ cơ sở b, đầu tiên số nguyên không âm b ^ N được biểu thị bằng chính xác N chữ số (bao gồm các số 0 đứng đầu) chỉ khi việc đánh số bắt đầu từ 0.
Điều này có thể được kiểm tra khá dễ dàng. Trong cơ sở 2, lấy 2^3 = 8
số thứ 8 là:
111
có thể được biểu diễn bằng 3
các bit, trong khi 1000
sẽ yêu cầu thêm một bit (4 bit).
Địa chỉ bộ nhớ máy tính có 2^N
các ô được giải quyết bằng N
bit. Bây giờ nếu chúng ta bắt đầu đếm ở 1, 2^N
các ô sẽ cần N+1
các dòng địa chỉ. Cần thêm bit để truy cập chính xác 1 địa chỉ. ( 1000
trong trường hợp trên.). Một cách khác để giải quyết nó là không thể truy cập địa chỉ cuối cùng và sử dụng N
các dòng địa chỉ.
Cả hai đều là giải pháp tối ưu phụ , so với bắt đầu đếm từ 0, sẽ giữ cho tất cả các địa chỉ có thể truy cập được, sử dụng N
các dòng địa chỉ chính xác !
0
Kể từ đó, quyết định bắt đầu tính đến tất cả các hệ thống kỹ thuật số , bao gồm cả phần mềm chạy trên chúng, bởi vì nó giúp mã đơn giản hơn để dịch sang những gì hệ thống cơ bản có thể diễn giải. Nếu không phải như vậy, sẽ có một thao tác dịch không cần thiết giữa máy và lập trình viên, cho mọi truy cập mảng. Nó làm cho việc biên dịch dễ dàng hơn.
Trích dẫn từ bài báo:
a[b]
đã được thực hiện như *(a+b)
trong các trình biên dịch ban đầu. Ngay cả ngày nay bạn vẫn có thể viết 2[a]
thay vì a[2]
. Bây giờ nếu các chỉ mục không bắt đầu từ 0 thì a[b]
sẽ biến thành *(a+b-1)
. Điều này sẽ yêu cầu 2 bổ sung trên CPU cùng lúc thay vì 0, nghĩa là một nửa tốc độ. Rõ ràng là không mong muốn.
Bởi vì 0 là khoảng cách từ con trỏ đến đầu mảng đến phần tử đầu tiên của mảng.
Xem xét:
int foo[5] = {1,2,3,4,5};
Để truy cập 0 chúng tôi làm:
foo[0]
Nhưng foo phân rã thành một con trỏ và truy cập ở trên có cách truy cập số học con trỏ tương tự
*(foo + 0)
Ngày nay, số học con trỏ không được sử dụng thường xuyên. Cách trở lại khi đó, đó là một cách thuận tiện để lấy địa chỉ và di chuyển X "ints" khỏi điểm bắt đầu. Tất nhiên nếu bạn muốn giữ nguyên vị trí của mình, bạn chỉ cần thêm 0!
Bởi vì chỉ số dựa trên 0 cho phép ...
array[index]
... được thực hiện như ...
*(array + index)
Nếu chỉ mục dựa trên 1, trình biên dịch sẽ cần phải tạo: *(array + index - 1)
và "-1" này sẽ làm giảm hiệu suất.
Bởi vì nó làm cho trình biên dịch và trình liên kết đơn giản hơn (dễ viết hơn).
"... Tham chiếu bộ nhớ theo địa chỉ và phần bù được thể hiện trực tiếp trong phần cứng trên hầu hết tất cả các kiến trúc máy tính, vì vậy chi tiết thiết kế này trong C giúp cho việc biên dịch dễ dàng hơn"
và
"... điều này làm cho việc thực hiện đơn giản hơn ..."
Chỉ số mảng luôn bắt đầu bằng zero. Giả sử địa chỉ cơ sở là 2000. Bây giờ arr[i] = *(arr+i)
. Bây giờ if i= 0
, điều này có nghĩa *(2000+0
) bằng với địa chỉ cơ sở hoặc địa chỉ của phần tử đầu tiên trong mảng. chỉ số này được coi là bù, vì vậy chỉ số bydeafault bắt đầu từ 0.
Vì lý do tương tự, khi đó là Thứ Tư và ai đó hỏi bạn bao nhiêu ngày cho đến Thứ Tư, bạn nói 0 chứ không phải 1, và khi đó là Thứ Tư và ai đó hỏi bạn bao nhiêu ngày cho đến Thứ Năm, bạn nói 1 thay vì 2.
Lời giải thích thanh lịch nhất mà tôi đã đọc về đánh số dựa trên số 0 là một quan sát rằng các giá trị không được lưu trữ tại các vị trí được đánh dấu trên dòng số, mà là trong các khoảng trắng giữa chúng. Mục đầu tiên được lưu trữ từ 0 đến 1, mục tiếp theo giữa một và hai, v.v ... Mục thứ N được lưu trữ giữa N-1 và N. Một loạt các mục có thể được mô tả bằng cách sử dụng các số ở hai bên. Các mục riêng lẻ được quy ước bằng cách sử dụng các số bên dưới nó. Nếu một được đưa ra một phạm vi (X, Y), xác định các số riêng lẻ bằng cách sử dụng số bên dưới có nghĩa là người ta có thể xác định mục đầu tiên mà không cần sử dụng bất kỳ số học nào (đó là mục X) nhưng người ta phải trừ một số từ Y để xác định mục cuối cùng (Y -1). Xác định các mục bằng cách sử dụng số ở trên sẽ giúp dễ dàng xác định mục cuối cùng trong phạm vi (đó sẽ là mục Y),
Mặc dù sẽ không có gì ghê gớm khi xác định các mục dựa trên số ở trên chúng, nhưng việc xác định mục đầu tiên trong phạm vi (X, Y) là mục trên X thường hoạt động độc đáo hơn so với xác định mục dưới đây (X + 1).
Lý do kỹ thuật có thể xuất phát từ thực tế là con trỏ đến vị trí bộ nhớ của một mảng là nội dung của phần tử đầu tiên của mảng. Dĩ nhiên, nếu bạn khai báo con trỏ có chỉ mục là một, các chương trình thường sẽ thêm giá trị của con trỏ đó vào con trỏ để truy cập nội dung không phải là điều bạn muốn, dĩ nhiên.
Hãy thử truy cập màn hình pixel bằng tọa độ X, Y trên ma trận dựa trên 1. Công thức hoàn toàn phức tạp. Tại sao lại phức tạp? Bởi vì cuối cùng bạn chuyển đổi các chuỗi X, Y thành một số, phần bù. Tại sao bạn cần chuyển đổi X, Y thành offset? Bởi vì đó là cách bộ nhớ được tổ chức bên trong máy tính, như một dòng tế bào bộ nhớ (mảng) liên tục. Làm thế nào máy tính đối phó với các tế bào mảng? Sử dụng offset (chuyển vị từ ô đầu tiên, mô hình lập chỉ mục dựa trên zero).
Vì vậy, tại một số điểm trong mã bạn cần (hoặc trình biên dịch cần) để chuyển đổi công thức 1 cơ sở thành công thức dựa trên 0 vì đó là cách máy tính xử lý bộ nhớ.
Giả sử chúng ta muốn tạo một mảng có kích thước 5
int mảng [5] = [2,3,5,9,8]
hãy để phần tử thứ 1 của mảng được trỏ vào vị trí 100
và chúng ta hãy xem xét việc lập chỉ mục bắt đầu từ 1 không phải từ 0.
bây giờ chúng ta phải tìm vị trí của phần tử thứ 1 với sự trợ giúp của chỉ mục
(hãy nhớ vị trí của phần tử thứ 1 là 100)
vì kích thước của một số nguyên là 4 bit
do đó -> xem xét chỉ số 1 vị trí sẽ có
kích thước của chỉ số (1) * kích thước của số nguyên (4) = 4
nên vị trí thực tế nó sẽ hiển thị cho chúng tôi là
100 + 4 = 104
Điều này không đúng vì vị trí ban đầu là 100.
nên chỉ đến 100 chứ không phải 104,
điều này là sai,
giả sử chúng ta đã lấy chỉ mục từ 0
thì
vị trí của phần tử thứ 1 phải là
kích thước của chỉ số (0) * kích thước của số nguyên (4) = 0
do đó ->
vị trí của phần tử thứ 1 là 100 + 0 = 100
và đó là vị trí thực tế của phần tử,
đây là lý do tại sao việc lập chỉ mục bắt đầu từ 0;
Tôi hy vọng nó sẽ làm rõ quan điểm của bạn.
Tôi đến từ một nền tảng Java. Tôi đã trình bày câu trả lời cho câu hỏi này trong sơ đồ bên dưới mà tôi đã viết trong một mảnh giấy tự giải thích
Các bước chính:
Lưu ý : Các khối hiển thị trong hình ảnh là biểu diễn bộ nhớ
trước hết bạn cần biết rằng các mảng được coi bên trong là con trỏ bởi vì "tên của mảng chứa địa chỉ của phần tử đầu tiên của mảng"
ex. int arr[2] = {5,4};
xem xét rằng mảng bắt đầu tại địa chỉ 100 vì vậy phần tử đầu tiên phần tử sẽ ở địa chỉ 100 và phần thứ hai sẽ ở 104 bây giờ, hãy xem xét nếu chỉ số mảng bắt đầu từ 1, vì vậy
arr[1]:-
điều này có thể được viết trong biểu thức con trỏ như thế này-
arr[1] = *(arr + 1 * (size of single element of array));
xem xét kích thước của int là 4byte, bây giờ,
arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);
như chúng ta đã biết tên mảng chứa địa chỉ của phần tử đầu tiên của nó, vì vậy, Array = 100 bây giờ,
arr[1] = *(100 + 4);
arr[1] = *(104);
cái nào cho
arr[1] = 4;
do biểu thức này, chúng tôi không thể truy cập phần tử tại địa chỉ 100 là phần tử chính thức đầu tiên,
Bây giờ hãy xem xét chỉ số mảng bắt đầu từ 0, vì vậy
arr[0]:-
điều này sẽ được giải quyết như
arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);
Bây giờ, chúng ta biết rằng tên mảng chứa địa chỉ của phần tử đầu tiên của nó, vì vậy,
arr[0] = *(100);
cho kết quả chính xác
arr[0] = 5;
do đó chỉ số mảng luôn bắt đầu từ 0 trong c.
tài liệu tham khảo: tất cả các chi tiết được viết trong cuốn sách "Ngôn ngữ lập trình C của brian kTHERhan và dennis ritchie"
Trong mảng, chỉ số cho biết khoảng cách từ phần tử bắt đầu. Vì vậy, phần tử đầu tiên nằm ở khoảng cách 0 từ phần tử bắt đầu. Vì vậy, đó là lý do tại sao mảng bắt đầu từ 0.
Đó là bởi vì address
phải trỏ sang phải element
trong mảng. Chúng ta hãy giả sử mảng dưới đây:
let arr = [10, 20, 40, 60];
Bây giờ chúng ta xem xét sự bắt đầu của địa chỉ hạnh phúc 12
và kích thước của element
be 4 bytes
.
address of arr[0] = 12 + (0 * 4) => 12
address of arr[1] = 12 + (1 * 4) => 16
address of arr[2] = 12 + (2 * 4) => 20
address of arr[3] = 12 + (3 * 4) => 24
Nếu không zero-based
, về mặt kỹ thuật, địa chỉ phần tử đầu tiên của chúng tôi array
sẽ bị 16
sai vì vị trí của nó 12
.
Tên mảng là một con trỏ không đổi trỏ đến địa chỉ cơ sở. Khi bạn sử dụng mảng [i] trình biên dịch sẽ thao tác nó dưới dạng * (Array + i) .Since int phạm vi là -128 đến 127, trình biên dịch nghĩ rằng -128 đến -1 là số âm và 0 đến 128 là số dương. Vì vậy, chỉ số mảng luôn bắt đầu bằng 0.
int
loại được yêu cầu để hỗ trợ ít nhất một phạm vi 16 bit và trên hầu hết các hệ thống ngày nay đều hỗ trợ 32 bit. Tôi nghĩ logic của bạn là thiếu sót, và câu trả lời của bạn thực sự không cải thiện các câu trả lời khác đã được cung cấp bởi người khác. Tôi đề nghị xóa cái này.