Câu hỏi Với mảng, tại sao lại là [5] == 5 [a]?


Như Joel đã chỉ ra trong Stack Overflow podcast # 34, trong Ngôn ngữ lập trình C (aka: K & R), có đề cập đến tài sản này của mảng trong C: a[5] == 5[a]

Joel nói rằng đó là vì số học con trỏ nhưng tôi vẫn không hiểu. Tại sao a[5] == 5[a]?


1429
2017-12-19 17:01


gốc


một cái gì đó giống như một [+] cũng làm việc như * (a ++) OR * (++ a)? - Egon
@Egon: Điều đó rất sáng tạo nhưng tiếc là đó không phải là cách trình biên dịch hoạt động. Trình biên dịch phiên dịch a[1] như một chuỗi các thẻ, không phải là chuỗi: * ({số nguyên vị trí của} một {toán tử} + {số nguyên} 1) giống như * ({integer} 1 {operator} + {số nguyên vị trí của} a) nhưng không phải là giống như * ({số nguyên vị trí của} một {toán tử} + {toán tử} +) - Dinah
Một biến thể hợp chất thú vị về điều này được minh họa trong Truy cập mảng không chính xác, ở đâu mà bạn có char bar[]; int foo[]; và foo[i][bar] được sử dụng như một biểu thức. - Jonathan Leffler
@EldritchConundrum, tại sao bạn nghĩ 'trình biên dịch không thể kiểm tra phần bên trái là một con trỏ'? Có, nó có thể. Đó là sự thật mà a[b] = *(a + b) cho bất kỳ a và b, nhưng đó là sự lựa chọn miễn phí của các nhà thiết kế ngôn ngữ + được định nghĩa giao hoán cho tất cả các loại. Không gì có thể ngăn cản họ cấm i + p trong khi cho phép p + i. - ach
@Andrey One thường kỳ vọng + để giao hoán, vì vậy có thể vấn đề thực sự là chọn làm cho hoạt động của con trỏ giống với số học, thay vì thiết kế toán tử bù riêng biệt. - Eldritch Conundrum


Các câu trả lời:


Tiêu chuẩn C xác định [] toán tử như sau:

a[b] == *(a + b)

vì thế a[5] sẽ đánh giá:

*(a + 5)

5[a] sẽ đánh giá:

*(5 + a)

a là một con trỏ đến phần tử đầu tiên của mảng. a[5] là giá trị 5 yếu tố hơn nữa từ a, giống như *(a + 5)và từ môn toán tiểu học, chúng ta biết những điều đó là bằng nhau (bổ sung là giao hoán).


1708
2017-12-19 17:04



Tôi tự hỏi nếu nó không giống như * ((5 * sizeof (a)) + a). Giải thích tuyệt vời mặc dù. - John MacIntyre
@Dinah: Từ góc độ C-trình biên dịch, bạn đã đúng. Không có sizeof là cần thiết và những biểu thức tôi đã đề cập là THE CÙNG. Tuy nhiên, trình biên dịch sẽ lấy sizeof vào tài khoản khi tạo mã máy. Nếu a là một mảng int, a[5]sẽ biên dịch thành một thứ như mov eax, [ebx+20] thay vì [ebx+5] - Mehrdad Afshari
@Dinah: A là một địa chỉ, nói 0x1230. Nếu a trong mảng int 32 bit, thì [0] là 0x1230, [1] là 0x1234, [2] ở 0x1238 ... a [5] tại x1244 vv. Nếu chúng ta chỉ thêm 5 vào 0x1230, chúng tôi nhận được 0x1235, đó là sai. - James Curran
@ sr105: Đó là trường hợp đặc biệt cho toán tử +, trong đó một toán hạng là một con trỏ và một số nguyên khác. Tiêu chuẩn nói rằng kết quả sẽ là loại con trỏ. Trình biên dịch / phải đủ / đủ thông minh. - aib
"từ môn toán tiểu học, chúng ta biết những điều đó là bình đẳng" - Tôi hiểu rằng bạn đang đơn giản hóa, nhưng tôi với những người cảm thấy như thế này là kết thúcđơn giản hóa. Nó không phải là tiểu học *(10 + (int *)13) != *((int *)10 + 13). Nói cách khác, có nhiều điều xảy ra ở đây hơn số học tiểu học. Tính tương đối phụ thuộc vào trình biên dịch nhận biết toán hạng nào là một con trỏ (và kích thước của đối tượng). Theo một cách khác, (1 apple + 2 oranges) = (2 oranges + 1 apple), nhưng (1 apple + 2 oranges) != (1 orange + 2 apples). - LarsH


Bởi vì truy cập mảng được định nghĩa theo các con trỏ. a[i] được định nghĩa là có nghĩa là *(a + i), giao hoán.


273
2017-12-19 17:05



Mảng không được định nghĩa theo các con trỏ, nhưng truy cập với họ là. - Lightness Races in Orbit
Tôi sẽ thêm "vì vậy nó bằng *(i + a), có thể được viết là i[a]". - Jim Balter
Tôi sẽ đề nghị bạn bao gồm các báo từ tiêu chuẩn, đó là như sau: 6.5.2.1: 2 Một biểu thức postfix theo sau bởi một biểu thức trong ngoặc vuông [] là một chỉ định subscripted của một phần tử của một đối tượng mảng. Định nghĩa của toán tử subscript [] là E1 [E2] giống hệt với (* ((E1) + (E2))). Do các quy tắc chuyển đổi áp dụng cho toán tử nhị phân +, nếu E1 là đối tượng mảng (tương đương, con trỏ tới phần tử ban đầu của đối tượng mảng) và E2 là số nguyên, E1 [E2] chỉ định phần tử E2 thứ E1 (đếm từ số không). - Vality
Để chính xác hơn: Mảng phân rã thành con trỏ khi bạn truy cập chúng. - 12431234123412341234123


Tôi nghĩ rằng một cái gì đó đang bị bỏ qua bởi các câu trả lời khác.

Vâng, p[i] theo định nghĩa tương đương với *(p+i), mà (vì cộng thêm là giao hoán) tương đương với *(i+p), một lần nữa, theo định nghĩa của [] toán tử) tương đương với i[p].

(Và trong array[i], tên mảng được chuyển đổi hoàn toàn thành con trỏ thành phần tử đầu tiên của mảng.)

Nhưng sự giao thoa của bổ sung không phải là tất cả những gì rõ ràng trong trường hợp này.

Khi cả hai toán hạng có cùng loại hoặc thậm chí các loại số khác nhau được quảng bá cho một loại phổ biến, tính tương giao tạo nên ý nghĩa hoàn hảo: x + y == y + x.

Nhưng trong trường hợp này, chúng ta đang nói cụ thể về số học con trỏ, trong đó một toán hạng là một con trỏ và một là một số nguyên. (Số nguyên + số nguyên là một phép toán khác, và con trỏ + con trỏ là vô nghĩa.)

Mô tả của tiêu chuẩn C về + nhà điều hành (N1570 6.5.6) nói:

Ngoài ra, cả hai toán hạng phải có loại số học hoặc một   toán hạng phải là một con trỏ đến một kiểu đối tượng hoàn chỉnh và một đối tượng khác   phải có loại số nguyên.

Nó có thể dễ dàng nói:

Ngoài ra, cả hai toán hạng phải có loại số học, hoặc Bên trái   toán hạng phải là một con trỏ đến một kiểu đối tượng hoàn chỉnh và toán hạng bên phải   phải có loại số nguyên.

trong trường hợp cả hai i + p và i[p] sẽ là bất hợp pháp.

Trong thuật ngữ C ++, chúng tôi thực sự có hai bộ quá tải + các toán tử, có thể được mô tả lỏng lẻo như sau:

pointer operator+(pointer p, integer i);

pointer operator+(integer i, pointer p);

trong đó chỉ có cái đầu tiên thực sự cần thiết.

Vậy tại sao nó theo cách này?

C ++ kế thừa định nghĩa này từ C, nhận được từ B (tính tương đối của việc lập chỉ mục mảng được đề cập rõ ràng trong năm 1972) Tham chiếu của người dùng đến B), đã nhận nó từ BCPL (hướng dẫn sử dụng ngày 1967), mà có thể đã nhận được nó từ các ngôn ngữ thậm chí trước đó (CPL? Algol?).

Vì vậy, ý tưởng rằng việc lập chỉ mục mảng được định nghĩa trong điều kiện bổ sung, và bổ sung đó, ngay cả một con trỏ và một số nguyên, là giao hoán, quay trở lại nhiều thập kỷ, với các ngôn ngữ tổ tiên của C.

Những ngôn ngữ này ít được đánh máy mạnh hơn C hiện đại. Đặc biệt, sự khác biệt giữa con trỏ và số nguyên thường bị bỏ qua. (Người lập trình C sớm đôi khi sử dụng con trỏ làm số nguyên không dấu, trước khi unsigned Vì vậy, ý tưởng của việc bổ sung không giao hoán vì các toán hạng có nhiều loại khác nhau có lẽ sẽ không xảy ra với các nhà thiết kế của các ngôn ngữ đó. Nếu người dùng muốn thêm hai "thứ", cho dù những thứ "" là số nguyên, con trỏ hay cái gì khác, nó không phải là ngôn ngữ để ngăn chặn nó.

Và trong những năm qua, bất kỳ thay đổi nào đối với quy tắc đó đều sẽ phá vỡ mã hiện tại (mặc dù tiêu chuẩn ANSI C 1989 có thể là một cơ hội tốt).

Thay đổi C và / hoặc C ++ để yêu cầu đặt con trỏ ở bên trái và số nguyên bên phải có thể phá vỡ một số mã hiện có, nhưng sẽ không mất đi sức mạnh biểu cảm thực sự.

Bây giờ chúng ta có arr[3] và 3[arr] có nghĩa là chính xác điều tương tự, mặc dù dạng sau không bao giờ xuất hiện bên ngoài IOCCC.


186
2017-08-23 01:37



Mô tả tuyệt vời về tài sản này. Từ góc độ cao, tôi nghĩ 3[arr] là một tạo phẩm thú vị nhưng hiếm khi được sử dụng. Câu trả lời được chấp nhận cho câu hỏi này (<stackoverflow.com/q/1390365/356>) mà tôi hỏi một lúc trở lại đã thay đổi cách tôi đã nghĩ về cú pháp. Mặc dù thường về mặt kỹ thuật không phải là một cách đúng và sai để làm những việc này, các loại tính năng này bắt đầu bạn suy nghĩ theo cách tách biệt với các chi tiết triển khai. Có lợi ích cho cách suy nghĩ khác nhau này một phần bị mất khi bạn khắc phục các chi tiết thực hiện. - Dinah
Bổ sung là giao hoán. Đối với các tiêu chuẩn C để xác định nó nếu không sẽ là lạ. Đó là lý do tại sao nó không thể dễ dàng nói "Ngoài ra, cả hai toán hạng phải có loại số học, hoặc toán hạng trái phải là một con trỏ đến một kiểu đối tượng hoàn chỉnh và toán hạng phải có kiểu số nguyên." - Điều đó sẽ không có ý nghĩa đối với hầu hết những người thêm mọi thứ. - iheanyi
@iheanyi: Bổ sung thường là giao hoán - và thường mất hai toán hạng cùng loại. Thêm con trỏ cho phép bạn thêm một con trỏ và một số nguyên, nhưng không phải là hai con trỏ. IMHO đã là một trường hợp đặc biệt đủ kỳ quặc đòi hỏi con trỏ phải là toán hạng bên trái sẽ không phải là một gánh nặng đáng kể. (Một số ngôn ngữ sử dụng "+" để nối chuỗi; điều đó chắc chắn không giao hoán.) - Keith Thompson
@supercat, Điều đó còn tệ hơn nữa. Điều đó có nghĩa là đôi khi x + 1! = 1 + x. Điều đó sẽ vi phạm hoàn toàn tài sản liên kết của việc bổ sung. - iheanyi
@iheanyi: Tôi nghĩ bạn có nghĩa là tài sản giao hoán; Ngoài ra đã không được kết hợp, vì trên hầu hết các triển khai (1LL + 1U) -2! = 1LL + (1U-2). Thật vậy, thay đổi sẽ làm cho một số tình huống kết hợp mà hiện tại không có, ví dụ: 3U + (UINT_MAX-2L) sẽ bằng (3U + UINT_MAX) -2. Điều tốt nhất là, mặc dù, là cho ngôn ngữ để có thêm các loại khác biệt mới cho các số nguyên có thể quảng bá và các vòng đại số "gói", để thêm 2 vào một ring16_t giữ 65535 sẽ mang lại ring16_t với giá trị 1, độc lập với kích thước của int. - supercat


Và dĩ nhiên

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

Lý do chính của việc này là vào những năm 70 khi C được thiết kế, các máy tính không có nhiều bộ nhớ (64KB là rất nhiều), do đó trình biên dịch C không thực hiện kiểm tra cú pháp nhiều. Vì thế "X[Y]"khá mù quáng được dịch sang"*(X+Y)"

Điều này cũng giải thích "+="và"++"cú pháp. Mọi thứ trong biểu mẫu"A = B + CTuy nhiên, nếu B là đối tượng tương tự như A, thì tối ưu hóa mức độ lắp ráp đã có sẵn. Nhưng trình biên dịch không đủ sáng để nhận ra nó, vì vậy nhà phát triển phải (A += C). Tương tự, nếu C là 1, một tối ưu hóa mức lắp ráp khác đã có sẵn, và một lần nữa nhà phát triển phải làm cho nó rõ ràng, bởi vì trình biên dịch đã không nhận ra nó. (Các trình biên dịch gần đây hơn làm, vì vậy những cú pháp này phần lớn không cần thiết trong những ngày này)


184
2017-12-19 17:07



Trên thực tế, điều đó đánh giá sai; thuật ngữ đầu tiên "ABCD" [2] == 2 ["ABCD"] ước lượng là đúng, hoặc 1, và 1! = 'C': D - Jonathan Leffler
@ Jonathan: cùng một sự mơ hồ dẫn đến việc chỉnh sửa tiêu đề gốc của bài đăng này. Chúng ta có tương đương với các điểm tương đương toán học, cú pháp mã hay mã giả hay không. Tôi tranh luận tương đương toán học nhưng vì chúng ta đang nói về mã, chúng ta không thể thoát khỏi việc chúng ta đang xem mọi thứ về cú pháp mã. - Dinah
Đây không phải là một huyền thoại sao? Tôi có nghĩa là các toán tử + = và ++ được tạo ra để đơn giản hóa cho trình biên dịch? Một số mã được rõ ràng hơn với họ, và nó là cú pháp hữu ích để có, không có vấn đề gì trình biên dịch không với nó. - Thomas Padron-McCarthy
+ = và ++ có một lợi ích đáng kể khác. nếu phía bên trái thay đổi một số biến trong khi được đánh giá, thay đổi sẽ chỉ được thực hiện một lần. a = a + ...; sẽ làm điều đó hai lần. - Johannes Schaub - litb
Không - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Dereferencing một chuỗi cung cấp cho bạn một char, không phải là một chuỗi con - MSalters


Một điều không ai dường như đã đề cập đến vấn đề của Dinah sizeof:

Bạn chỉ có thể thêm một số nguyên vào một con trỏ, bạn không thể thêm hai con trỏ với nhau. Bằng cách đó khi thêm một con trỏ vào một số nguyên, hoặc một số nguyên cho một con trỏ, trình biên dịch luôn biết bit nào có kích thước cần được tính đến.


51
2018-02-11 15:56



Có một cuộc trò chuyện khá đầy đủ về điều này trong các ý kiến ​​của câu trả lời được chấp nhận. Tôi đã tham khảo cuộc trò chuyện đã nói trong bản chỉnh sửa cho câu hỏi ban đầu nhưng không trực tiếp giải quyết mối quan tâm rất hợp lệ của bạn về sizeof. Bạn không chắc chắn cách làm tốt nhất trong SO. Tôi có nên thực hiện một chỉnh sửa khác cho orig không. câu hỏi? - Dinah


Để trả lời câu hỏi theo nghĩa đen. Nó không phải lúc nào cũng đúng x == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

bản in

false

46
2017-08-11 13:50



Trên thực tế, "nan" không bằng chính nó: cout << (a[5] == a[5] ? "true" : "false") << endl; Là false. - TrueY
@TrueY: Anh ấy đã nêu rõ rằng đối với trường hợp NaN (và cụ thể là x == x không phải lúc nào cũng đúng). Tôi nghĩ đó là ý định của anh ấy. Anh ấy là kỹ thuật đúng (và có thể, như họ nói, loại tốt nhất của chính xác!). - Tim Čas
Câu hỏi là về C, mã của bạn không phải là mã C. Cũng có một NAN trong <math.h>, tốt hơn 0.0/0.0, bởi vì 0.0/0.0 là UB khi __STDC_IEC_559__ không được định nghĩa (Hầu hết các triển khai không xác định __STDC_IEC_559__, nhưng trên hầu hết các triển khai 0.0/0.0 vẫn sẽ hoạt động) - 12431234123412341234123


Câu hỏi / câu trả lời hay.

Chỉ muốn chỉ ra rằng C con trỏ và mảng không phải là tương tự, mặc dù trong trường hợp này sự khác biệt là không cần thiết.

Xem xét các khai báo sau đây:

int a[10];
int* p = a;

Trong a.out, biểu tượng một là địa chỉ bắt đầu của mảng và biểu tượng p là địa chỉ nơi con trỏ được lưu trữ và giá trị của con trỏ tại vị trí bộ nhớ đó là phần đầu của mảng.


23
2017-12-20 08:16



Không, về mặt kỹ thuật chúng không giống nhau. Nếu bạn định nghĩa một số b là int * const và làm cho nó trỏ tới một mảng, nó vẫn là một con trỏ, có nghĩa là trong bảng ký hiệu, b tham chiếu đến một vị trí bộ nhớ lưu trữ một địa chỉ. . - PolyThinker
Rất tốt. Tôi nhớ có một lỗi rất khó chịu khi tôi xác định một biểu tượng toàn cầu như char s [100] trong một mô-đun, khai báo nó là extern char * s; trong một mô-đun khác. Sau khi liên kết tất cả lại với nhau, chương trình hoạt động rất kỳ lạ. Bởi vì các mô-đun bằng cách sử dụng khai báo extern đã sử dụng byte ban đầu của mảng như một con trỏ đến char. - Giorgio
Ban đầu, trong C của ông bà BCPL, một mảng là một con trỏ. Đó là, những gì bạn nhận được khi bạn viết (tôi đã chuyển ngữ sang C) int a[10] là một con trỏ được gọi là 'a', trỏ đến đủ cửa hàng cho 10 số nguyên, ở nơi khác. Do đó, + i và j + i có cùng dạng: thêm nội dung của một vài vị trí bộ nhớ. Trong thực tế, tôi nghĩ BCPL là không có chữ, vì vậy chúng giống hệt nhau. Và quy mô loại sizeof không áp dụng, vì BCPL hoàn toàn là từ định hướng (trên các máy định dạng word). - dave
Tôi nghĩ cách tốt nhất để hiểu sự khác biệt là so sánh int*p = a; đến int b = 5;  Trong phần sau, "b" và "5" là cả hai số nguyên, nhưng "b" là một biến, trong khi "5" là một giá trị cố định. Tương tự, "p" & "a" là cả hai địa chỉ của một ký tự, nhưng "a" là một giá trị cố định. - James Curran


Tôi chỉ tìm ra cú pháp xấu xí này có thể là "hữu ích", hoặc ít nhất là rất thú vị để chơi khi bạn muốn đối phó với một mảng các chỉ mục tham chiếu đến các vị trí trong cùng một mảng. Nó có thể thay thế các dấu ngoặc vuông lồng nhau và làm cho mã dễ đọc hơn!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Tất nhiên, tôi khá chắc chắn rằng không có trường hợp sử dụng cho rằng trong mã thực, nhưng tôi thấy nó thú vị anyway :)


21
2018-06-10 19:50



Ôi Chúa ơi!!! làm thế nào ai đó có thể nói rằng thích rằng ký hiệu !!! Nó đau mắt tôi !!! - Luis Colorado
Khi bạn thấy i[a][a][a] bạn nghĩ rằng tôi hoặc là một con trỏ đến một mảng hoặc một mảng của một con trỏ đến một mảng hoặc một mảng ... và a là một chỉ mục. Khi bạn thấy a[a[a[i]]], bạn nghĩ rằng một là một con trỏ đến một mảng hoặc một mảng và i là một chỉ mục. - 12431234123412341234123
Wow! Việc sử dụng tính năng "ngu ngốc" này rất thú vị. Có thể hữu ích trong cuộc thi thuật toán trong một số vấn đề)) - Serge Breusov


Đối với con trỏ trong C, chúng ta có

a[5] == *(a + 5)

và cũng

5[a] == *(5 + a)

Do đó nó là sự thật a[5] == 5[a].


17
2018-03-23 07:05





Không phải là câu trả lời, nhưng chỉ là một số thức ăn để suy nghĩ. Nếu lớp có toán tử chỉ mục / chỉ số quá tải, biểu thức 0[x] sẽ không làm việc:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Vì chúng tôi không có quyền truy cập vào int lớp học, điều này không thể được thực hiện:

class int
{
   int operator[](const Sub&);
};

14
2018-06-19 08:37



class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } }; - Ben Voigt
Bạn đã thực sự cố gắng biên dịch nó chưa? Có một số toán tử không thể được triển khai bên ngoài lớp (tức là các hàm không tĩnh)! - Ajay
oops, bạn nói đúng. "operator[] sẽ là một hàm thành viên không tĩnh với chính xác một tham số. "Tôi đã quen thuộc với giới hạn đó operator=, không nghĩ rằng nó được áp dụng cho []. - Ben Voigt
Tất nhiên, nếu bạn thay đổi định nghĩa [] nhà điều hành, nó sẽ không bao giờ tương đương nữa ... nếu a[b] bằng *(a + b) và bạn thay đổi điều này, bạn cũng sẽ phải quá tải int::operator[](const Sub&); và int không phải là một lớp ... - Luis Colorado
Đây ... không phải ... C. - MD XF


Nó có lời giải thích rất tốt trong TUTORIAL ON POINTERS AND ARRAYS TRONG C bởi Ted Jensen.

Ted Jensen giải thích nó như sau:

Trong thực tế, điều này là đúng, tức là bất cứ nơi nào một người viết a[i] nó có thể   thay thế bằng *(a + i)  mà không có bất kỳ vấn đề. Trong thực tế, trình biên dịch   sẽ tạo cùng một mã trong cả hai trường hợp. Vì vậy, chúng ta thấy con trỏ đó   số học là điều tương tự như lập chỉ mục mảng. Hoặc là cú pháp tạo   cùng một kết quả.

Điều này là không nói rằng con trỏ và mảng   là những điều tương tự, họ không. Chúng tôi chỉ nói rằng để xác định   một phần tử đã cho của một mảng, chúng ta có sự lựa chọn của hai cú pháp, một   bằng cách sử dụng chỉ mục mảng và phần còn lại sử dụng số học con trỏ,   mang lại kết quả giống hệt nhau.

Bây giờ, nhìn vào điều cuối cùng này   biểu hiện, một phần của nó .. (a + i), là một bổ sung đơn giản bằng cách sử dụng nút +   toán tử và các quy tắc của trạng thái C là biểu thức như vậy là   giao hoán. Tức là (a + i) giống hệt nhau (i + a). Vì vậy chúng tôi có thể   viết *(i + a) dễ dàng như *(a + i).   Nhưng *(i + a) có thể đến từ i[a] ! Từ tất cả những điều này đến tò mò   sự thật rằng nếu:

char a[20];

viết

a[3] = 'x';

giống như viết

3[a] = 'x';

9
2017-09-27 06:46



a + i KHÔNG phải là bổ sung đơn giản, bởi vì nó là số học con trỏ. nếu kích thước của phần tử là 1 (char), thì có, nó giống như số nguyên +. Nhưng nếu nó (ví dụ) một số nguyên, thì nó có thể tương đương với một + 4 * i. - Alex Brown
@AlexBrown Có, nó là số học con trỏ, đó là chính xác lý do tại sao câu cuối cùng của bạn là sai, trừ khi bạn lần đầu tiên cast 'a' là một (char *) (giả sử rằng một int là 4 ký tự). Tôi thực sự không hiểu tại sao rất nhiều người bị treo lên trên kết quả giá trị thực tế của số học con trỏ. Mục đích toàn bộ số học của con trỏ là trừu tượng hóa các giá trị con trỏ bên dưới và để cho lập trình viên suy nghĩ về các đối tượng đang được thao tác thay vì các giá trị địa chỉ. - jschultz410