Tại sao c = ++ (a + b) cho lỗi biên dịch?


111

Sau khi nghiên cứu, tôi đọc rằng toán tử tăng dần yêu cầu toán hạng phải có đối tượng dữ liệu có thể sửa đổi: https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

Từ điều này, tôi đoán rằng nó gây ra lỗi biên dịch vì (a+b)là một số nguyên tạm thời và vì vậy không thể sửa đổi được.

Cách hiểu này có đúng không? Đây là lần đầu tiên tôi cố gắng nghiên cứu một vấn đề vì vậy nếu có điều gì đáng lẽ tôi nên tìm kiếm, xin hãy tư vấn.


35
Điều đó không tệ về mặt nghiên cứu. Bạn đang đi đúng hướng.
StoryTeller - Unslander Monica

35
Bạn mong đợi biểu thức để làm gì?
qrdl

4
theo tiêu chuẩn C11 6.5.3.1: Các toán hạng của thặng dư tiền tố hoặc điều hành sụt lần sẽ có nguyên tử, có trình độ, hoặc không đủ điều kiện thực hoặc kiểu con trỏ, và sẽ trở thành một giá trị trái modifiable
Christian Gibbons

10
Bạn muốn số 1 được phân phối như thế nào giữa a và b? "Chỉ số mảng có nên bắt đầu từ 0 hay 1 không? Thỏa hiệp 0,5 của tôi đã bị từ chối mà không có sự cân nhắc thích hợp." - Stan Kelly-Bootle
Andrew Morton

5
Tôi nghĩ rằng một câu hỏi tiếp theo là tại sao bạn lại muốn làm điều này khi c = a + b + 1làm cho ý định của bạn rõ ràng hơn và cũng ngắn hơn để nhập. Các toán tử tăng / giảm thực hiện hai việc 1. chúng và đối số của chúng tạo thành một biểu thức (có thể được sử dụng, ví dụ như trong vòng lặp for), 2. chúng sửa đổi đối số. Trong ví dụ của bạn, bạn đang sử dụng thuộc tính 1. nhưng không phải thuộc tính 2., vì bạn loại bỏ đối số đã sửa đổi. Nếu bạn không cần thuộc tính 2. và chỉ muốn biểu thức, thì bạn có thể viết một biểu thức, ví dụ: x + 1 thay vì x ++.
Trevor

Câu trả lời:


117

Đó chỉ là một quy tắc, vậy thôi, và có thể ở đó (1) giúp việc viết trình biên dịch C dễ dàng hơn và (2) chưa ai thuyết phục được ủy ban tiêu chuẩn C nới lỏng nó.

Nói một cách không chính thức, bạn chỉ có thể viết ++foonếu foocó thể xuất hiện ở bên trái của biểu thức gán như foo = bar. Vì bạn không thể viết a + b = bar, bạn cũng không thể viết ++(a + b).

Không có lý do thực sự tại sao a + bkhông thể mang lại tạm thời mà ++có thể hoạt động và kết quả của việc đó là giá trị của biểu thức ++(a + b).


4
Tôi nghĩ điểm (1) trúng đinh vào đầu. Chỉ cần nhìn vào các quy tắc để hiện thực hóa tạm thời trong C ++ có thể khiến một người đau đầu (nhưng nó rất mạnh mẽ, phải nói như vậy).
StoryTeller - Unslander Monica

4
@StoryTeller: Thật vậy, không giống như ngôn ngữ yêu thích của chúng tôi C ++, C vẫn biên dịch thành hợp ngữ tương đối nhỏ.
Bathsheba

29
Đây là một lý do thực sự của IMHO: sẽ là một sự nhầm lẫn đáng sợ nếu ++đôi khi có tác dụng phụ của việc sửa đổi một cái gì đó và đôi khi không.
aschepler

5
@dng: Quả thực là như vậy; đó là lý do tại sao các thuật ngữ lvalue và rvalue được giới thiệu, mặc dù mọi thứ phức tạp hơn ngày nay (đặc biệt là trong C ++). Ví dụ, một hằng số không bao giờ có thể là một giá trị: một cái gì đó như 5 = a không có ý nghĩa gì.
Bathsheba

6
@Bathsheba Điều đó giải thích tại sao 5 ++ cũng gây ra lỗi biên dịch
dng

40

Tiêu chuẩn C11 nêu trong phần 6.5.3.1

Toán hạng của toán tử tăng hoặc giảm tiền tố phải có kiểu thực hoặc con trỏ nguyên tử, đủ điều kiện hoặc không đủ tiêu chuẩn và phải là giá trị có thể sửa đổi

Và "giá trị có thể sửa đổi" được mô tả trong phần 6.3.2.1, tiểu mục 1

Giá trị là một biểu thức (với một kiểu đối tượng khác với void) có khả năng chỉ định một đối tượng; nếu một lvalue không chỉ định một đối tượng khi nó được đánh giá, thì hành vi đó là không xác định. Khi một đối tượng được cho là có một kiểu cụ thể, kiểu đó được chỉ định bởi giá trị được sử dụng để chỉ định đối tượng. Giá trị có thể sửa đổi là giá trị không có kiểu mảng, không có kiểu không hoàn chỉnh, không có kiểu đủ điều kiện const và nếu nó là một cấu trúc hoặc liên hợp, thì không có bất kỳ thành viên nào (bao gồm, đệ quy, bất kỳ thành viên nào hoặc phần tử của tất cả các tập hợp hoặc kết hợp được chứa) với loại đủ điều kiện const.

Vì vậy, (a+b)không phải là giá trị có thể sửa đổi và do đó không đủ điều kiện cho toán tử tăng tiền tố.


1
Kết luận của bạn từ những định nghĩa này còn thiếu ... Bạn muốn nói rằng (a + b) không có khả năng chỉ định một đối tượng, nhưng những đoạn văn này không cho phép điều đó.
hkBst

21

Bạn nói đúng. những ++cố gắng gán giá trị mới cho biến ban đầu. Vì vậy, ++asẽ lấy giá trị của a, thêm 1vào nó và sau đó gán nó trở lại a. Vì, như bạn đã nói, (a + b) là một giá trị tạm thời và không phải là một biến có địa chỉ bộ nhớ được chỉ định nên không thể thực hiện việc gán.


12

Tôi nghĩ rằng bạn chủ yếu trả lời câu hỏi của riêng bạn. Tôi có thể thực hiện một thay đổi nhỏ đối với cách viết của bạn và thay thế "biến tạm thời" bằng "rvalue" như C.Gibbons đã đề cập.

Các điều khoản thay đổi, tranh luận, biến tạm thời và như vậy sẽ trở nên rõ ràng hơn khi bạn tìm hiểu về mô hình bộ nhớ C (điều này trông giống như một cái nhìn tổng quan tốt đẹp: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).

Thuật ngữ "rvalue" có vẻ không rõ ràng khi bạn mới bắt đầu, vì vậy tôi hy vọng những điều sau đây sẽ giúp phát triển trực giác về nó.

Giá trị / giá trị đang nói về các cạnh khác nhau của một dấu bằng (toán tử gán): lvalue = phía bên trái (chữ L viết thường, không phải "một") rvalue = phía bên phải

Tìm hiểu một chút về cách C sử dụng bộ nhớ (và các thanh ghi) sẽ hữu ích để hiểu tại sao sự khác biệt lại quan trọng. Trong các nét vẽ rộng , trình biên dịch tạo một danh sách các lệnh ngôn ngữ máy tính toán kết quả của một biểu thức (giá trị) và sau đó đặt kết quả đó ở đâu đó (giá trị). Hãy tưởng tượng một trình biên dịch xử lý đoạn mã sau:

x = y * 3

Trong mã giả lắp ráp, nó có thể trông giống như ví dụ đồ chơi này:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

Toán tử ++ (và - đối của nó) cần một "nơi nào đó" để sửa đổi, về cơ bản là bất kỳ thứ gì có thể hoạt động như một giá trị.

Việc hiểu mô hình bộ nhớ C sẽ rất hữu ích vì bạn sẽ có ý tưởng tốt hơn trong đầu về cách các đối số được truyền cho các hàm và (cuối cùng) cách làm việc với cấp phát bộ nhớ động, như hàm malloc (). Vì những lý do tương tự, bạn có thể học lập trình hợp ngữ đơn giản tại một số điểm để hiểu rõ hơn về những gì trình biên dịch đang làm. Ngoài ra, nếu bạn đang sử dụng gcc , tùy chọn -S "Dừng sau giai đoạn biên dịch thích hợp; không lắp ráp." có thể rất thú vị (mặc dù tôi khuyên bạn nên cố gắng nó trên một nhỏ đoạn mã).

Chỉ là một bên: Lệnh ++ đã xuất hiện từ năm 1969 (mặc dù nó bắt đầu từ tiền thân của C, B):

(Ken Thompson's) quan sát (là) rằng phép tịnh tiến của ++ x nhỏ hơn phép tịnh tiến của x = x + 1. "

Tiếp theo tài liệu tham khảo wikipedia đó sẽ đưa bạn đến một bài viết thú vị của Dennis Ritchie (chữ "R" trong "K&R C") về lịch sử của ngôn ngữ C, được liên kết tại đây để thuận tiện: http://www.bell-labs.com/ usr / dmr / www / chist.html nơi bạn có thể tìm kiếm "++".


6

Lý do là tiêu chuẩn yêu cầu toán hạng là một giá trị. Biểu thức (a+b)không phải là giá trị, vì vậy không được phép áp dụng toán tử tăng dần.

Bây giờ, người ta có thể nói "OK, đó là thực sự là lý do, nhưng có thực sự không có * * thật lý do nào khác hơn thế" , nhưng thật không may mắn từ ngữ đặc biệt về cách thức điều hành làm việc với thực tế không đòi hỏi phải là trường hợp.

Biểu thức ++ E tương đương với (E + = 1).

Rõ ràng, bạn không thể viết E += 1nếu Ekhông phải là giá trị. Đó là một điều đáng tiếc bởi vì người ta cũng có thể nói: "tăng E lên một" và được thực hiện. Trong trường hợp đó, việc áp dụng toán tử trên một giá trị không phải (về nguyên tắc) là hoàn toàn có thể thực hiện được, với chi phí làm cho trình biên dịch phức tạp hơn một chút.

Bây giờ, định nghĩa có thể được sửa lại một cách đáng kinh ngạc (tôi nghĩ nó thậm chí không phải là C ban đầu mà là một vật gia truyền của B), nhưng làm như vậy về cơ bản sẽ thay đổi ngôn ngữ thành một thứ không còn tương thích với các phiên bản cũ của nó. Vì lợi ích có thể là khá nhỏ nhưng hệ lụy có thể xảy ra là rất lớn, điều đó không bao giờ xảy ra và có lẽ sẽ không bao giờ xảy ra.

Nếu bạn xem xét C ++ ngoài C (câu hỏi được gắn thẻ C, nhưng đã có thảo luận về quá tải toán tử), câu chuyện thậm chí còn trở nên phức tạp hơn. Trong C, thật khó để tưởng tượng rằng điều này có thể xảy ra, nhưng trong C ++, kết quả của (a+b)rất có thể là thứ mà bạn không thể tăng lên chút nào, hoặc tăng có thể có tác dụng phụ rất đáng kể (không chỉ thêm 1). Trình biên dịch phải có khả năng đối phó với điều đó và chẩn đoán các trường hợp có vấn đề khi chúng xảy ra. Trên một lvalue, điều đó vẫn còn hơi nhỏ để kiểm tra. Không phải vậy đối với bất kỳ loại biểu thức lộn xộn nào bên trong dấu ngoặc đơn mà bạn ném vào điều tồi tệ.
Đây không phải là lý do thực sự khiến nó không thể được thực hiện, nhưng nó chắc chắn là một lời giải thích tại sao những người thực hiện điều này không thực sự xuất sắc khi thêm một tính năng hứa hẹn rất ít lợi ích cho rất ít người.



3

++ cố gắng cung cấp giá trị cho biến ban đầu và vì (a + b) là giá trị tạm thời nên nó không thể thực hiện thao tác. Và chúng về cơ bản là các quy tắc của quy ước lập trình C để giúp việc lập trình trở nên dễ dàng. Đó là nó.


2

Khi biểu thức ++ (a + b) được thực hiện, thì ví dụ:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.