Pointer arithmetic là gì?

E có đọc cuốn Head First C thì ở trang 103 - 105 có nói vì sao chỉ số mảng thường bắt đầu từ 0, và nêu lý do là pointer arithmetic. Tuy nhiên, trình độ tiếng Anh của em chưa được tốt lắm nên còn rất mơ hồ đoạn đó, cụ thể là về pointer arithmetic và “lý do chỉ số index của array bắt đầu từ 0”.
Search trên DNH thì chưa có topic nào nói cả, trên Google thì lại toàn ENGLISH :cry:
Nên nhờ mọi người thông não giúp e 2 vấn đề nói trên ạ, nói kỹ nhé chứ nói tắt hay cao siêu quá là e k hiểu âu :cry:
E cảm ơn các pro trc :blush:

1 Like

Pointer arithmetic gồm bốn (đếm mới ra 5) con toán: ++, --, +: (nZ x Z) -> nZ, +: (Z x nZ) -> nZ và cuối cùng, mà cũng ít dùng là -: (nZ x nZ) -> Z.

Đầu tiên cần nhắc lại chút là nội dung của pointer là một địa chỉ, hay pointer trỏ đến một ô nhớ có địa chỉ, hay ta gọi là slot. Khi ta ++ một pointer thì nên hiểu rằng ta muốn trỏ đến slot liền sau nó. Nếu một slot là 8 byte thì khi ++ một pointer nghĩa là tăng nó lên 8 đơn vị. Ngoài ra địa chỉ phải là bội của size (yêu cầu alignment) nên ta có thể kí hiệu là (nó thuộc) nZ. – cũng hiểu tương tự.

Mảng sinh ra chính từ hai con toán tiếp theo. Nghĩa là bạn viết a[j] hay j[a] đều đúng cả nhưng chỉ đc viết mảng trước chỉ số sau mà thôi, do toán tử [] chỉ hỗ trợ overload mỗi dạng này. Vậy thực ra chỉ có 3 con toán cần nhớ.

Trở lại câu hỏi. a[1] tức là *(a+1) mà lại là ô nhớ đầu tiên của a thì có vấn đề. Nếu vậy thì *(a+0) phải truy cập slot đằng sau a (lỗi!) Vậy *(a+0) != *a và dẫn đến tự mâu thuẫn. Ngoài ra ngay từ đầu ta muốn a++ = a+1 chứ không phải a! Khi bạn đã quen với lập trình thì sẽ thấy đếm từ 1 chỉ là do thói quen thôi.

5 Likes

E chưa hiểu 2 chỗ này lắm @@

Hi Long Dragon.
Việc chỉ số bắt đầu từ 0 chỉ là một cách cài đặt ngôn ngữ mà thôi.
https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(array) (Một số cái vẫn bắt đầu bằng 1 như bình thường.)
Và việc bắt đầu 1 hay 0 thì các nhà toán học vẫn có nhiều ý kiến. (Bạn GG “Why numbering should start at 0”).
Tuy nhiên trong C lựa chon 0 dự theo bản chất cài đặt mảng của C.
Tên mảng là con trỏ trỏ đến vùng nhớ đầu tiên của mảng. (Tại sao không phải giữa hay cuối bạn tự suy nghĩ.) array[0] = *array.

1 Like

Chắc bạn từng biết kí hiệu: f: R -> R là hàm số thực :slight_smile: … mà giải thích cái này thấy hơi lạc đề.
Một phép toán hai ngôi trong S sẽ kí hiệu là *: S x S -> S. Phép x (tích Descartes) này tạo ra các cặp (a, b) từ mỗi toán hạng. Mỗi cặp này ứng với một phần tử thuộc S.

Tức là nếu 1 cấu trúc có kích cỡ là 8 byte thì mọi con trỏ trỏ vào nó phải chia hết cho 8. Vậy gọi nó là 8Z thôi.

1 Like

nói dễ hiểu là cho địa chỉ nhà p = 500, máy nó hiểu p[1] là tới căn nhà cách p 1 căn, ví dụ là nhà 502. p[10] là nhà cách căn nhà số 500 10 căn, hay là nhà số 520. Nó ko hiểu từ 500-520 có 11 căn nhà đánh số từ 1 tới 11. Nó chỉ biết là nhà cách căn số 500 10 căn. Vậy nên lấy index từ 0 cho máy nó dễ hiểu.

chắc giải thích này cũng khó hiểu, tại nói theo kiểu tiếng Việt thì [A][B][C], căn nhà C mới là căn nhà cách nhà A 1 căn :joy:

ví dụ khác cho thấy bắt đầu từ 0 khá tiện: phép chia lấy dư cho số n bất kì luôn cho kết quả 0,1,2,…,n-1, vừa vặn với index bắt đầu từ 0

ví dụ mỗi căn nhà có chiều rộng 4m, nhà số 1 bắt đầu ở mốc 0 mét. Hỏi nhà thứ 3 bắt đầu ở mét thứ mấy? Câu trả lời là mét thứ 8. Cách tính đơn giản là lấy mốc căn nhà đầu tiên + 4 * (3 - 1). Vậy lấy địa chỉ căn thứ x phải lấy 4(x-1). Sao ko chọn hẳn index từ 0 luôn cho khỏi phải -1.

2 Likes

Một ví dụ là cột cây số. Ngay đầu đường thì cột cây số phải là 0, rồi mới tới 1, 2, … tức là 1, 2, … km từ đầu đường nhưng đếm số cột thì phải là 1, 2, 3, …

2 Likes

Trong bóng đá, một trận đấu có 90 phút (Mảng A có 90 phần tử)
Phút 0 là phút thứ nhất => A[0] là phần tử đầu tiên
Phút 89 là phút thứ 90 => A[89] là phần tử cuối cùng
Hết 90 phút là hết giờ => nếu index lớn hơn 89 thì mảng A kết thúc

Tất nhiên, nếu thích trận đấu bóng đá kéo dài 90 phút từ 01:00 đến 90:59 cũng không vấn đề gì, giống như cách đếm mảng quy ước trong một số ngôn ngữ như Pascal, bắt đầu từ 1 đến n

Việc quy ước phần tử đầu tiên là 0 có cái lợi như sau:
Con trỏ cho mảng A trỏ vào ô nhớ A chẳng hạn, thì phần tử A[0] được lưu ở A, có giá trị là *A, phần tử A[1] lưu ở A+1 và có giá trị là *(A+1), phần tử A[n] có giá trị là *(A+n)

2 Likes

Công bằng mà nói thì a[i] vẫn có thể định nghĩa bằng *(a+i-1) (nhưng mà đánh số từ 0 riết quen). Người viết trình biên dịch mới khổ :slight_smile: thay vì lưu a giờ phải lưu a-1 (hehe).

1 Like

Cũng chưa đc thông não cho lém :yum:
Nhưng tóm lại thì Pointer Arithmetic là cái j thế các pro ?

Còn về vấn đề vì sao chỉ số index của array bắt đầu từ 0 thì qua các câu tl của mấy bạn trên thì e cũng hiểu đc sơ sơ rồi !

Mình đọc qua một số tài liệu thì có vẻ Pointer Arithmetic là việc tính toán và thay đổi địa chỉ của con trỏ bằng các phép toán số học

Các phép tính trên con trỏ.

E chưa hiểu 2 chỗ này lém a @rogp10 :slight_smile:

a[j] := *(a+j) == *(j+a) =: j[a].

1 Like

:==: là cái j thế a?

Định nghĩa đó bạn.

(Ngoài lề) Sau này mình thấy nếu cho a[j] := *(a+j-1) thì cũng… đâu có gì đâu :smiley:

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?