Kết quả của + = trong C và C ++ là gì?


93

Tôi có mã sau:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

Nếu tôi cố gắng biên dịch nó dưới dạng nguồn C bằng gcc, tôi gặp lỗi:

error: lvalue required as left operand of assignment

Nhưng nếu tôi biên dịch nó dưới dạng nguồn C ++ bằng g ++, tôi không gặp lỗi và khi tôi chạy tệp thực thi:

i = 20

Tại sao các hành vi khác nhau?


85
Ngôn ngữ khác nhau, quy tắc cú pháp khác nhau ?. Cá nhân, tôi sẽ từ chối mã đó trong quá trình xem xét mã.
Tối đa

7
Tránh mã như thế này imo ... Không rõ ràng cho mọi người.
allaire

1
Không nghi ngờ gì nữa, mã không sạch và cần tránh trong quá trình phát triển "thực". Nhưng tuy nhiên, tôi quan sát hành vi tương tự và muốn biết lý do của nó.
ulidtko

9
Đây KHÔNG phải là một đoạn mã từ một phần mềm thực sự. Đây chỉ là một đường gấp khúc mà tôi vô tình vấp phải.
Svetlin Mladenov

3
@JohnDibling Tôi nghĩ lượt ủng hộ cụ thể là (i + = 10) + = 10 Tôi không biết ngôn ngữ nào là mã hợp pháp và việc anh ấy nói C ++ thực sự biên dịch nó khiến tôi tò mò.
Tony318

Câu trả lời:


133

Ngữ nghĩa của các toán tử gán ghép khác nhau trong C và C ++:

Tiêu chuẩn C99, 6.5.16, phần 3:

Toán tử gán lưu trữ một giá trị trong đối tượng được chỉ định bởi toán hạng bên trái. Một biểu thức gán có giá trị của toán hạng bên trái sau phép gán, nhưng không phải là giá trị.

Trong C ++ 5.17.1:

Toán tử gán (=) và toán tử gán ghép đều nhóm từ phải sang trái. Tất cả đều yêu cầu giá trị có thể modi làm toán hạng bên trái của chúng và trả về giá trị có kiểu và giá trị của toán hạng bên trái sau khi thực hiện xong phép gán.

CHỈNH SỬA: Hành vi của (i+=10)+=10trong C ++ không được xác định trong C ++ 98, nhưng được xác định rõ trong C ++ 11. Xem câu trả lời này cho câu hỏi của NPE để biết các phần liên quan của tiêu chuẩn.


Chính xác. Một trả về giá trị kết quả và một trả về biến (địa chỉ)
texasbruce

7
Quan trọng : Lưu ý rằng đó (i+=10)+=10là hành vi không xác định trong C ++, hãy xem câu trả lời @aix.
David Rodríguez - dribeas

@ DavidRodríguez-dribeas Ý của bạn là không xác định , không phải không xác định , phải không?
dasblinkenlight

4
@dasblinkenlight: Không, ý anh ấy là không xác định . Trong C ++ 03 trở về trước, việc sửa đổi kết quả giá trị của một biểu thức hoạt động không thể đoán trước trong tất cả các trình biên dịch do thiếu điểm trình tự can thiệp. Nếu nó không được xác định , nó sẽ hoạt động có thể đoán trước nhưng khác nhau trên các trình biên dịch khác nhau .
Justin ᚅᚔᚈᚄᚒᚔ

2
Nó sẽ hữu ích trong một cài đặt như int f(int &y); f(x += 10);- chuyển một tham chiếu đến biến đã sửa đổi vào một hàm.
Phil Miller

51

Ngoài mã C không hợp lệ, dòng

(i+=10)+=10;

sẽ dẫn đến hành vi không xác định trong cả C và C ++ 03 vì nó sẽ sửa đổi ihai lần giữa các điểm trình tự.

Về lý do tại sao nó được phép biên dịch trong C ++:

[C ++ N3242 5.17.1] Toán tử gán (=) và toán tử gán ghép đều nhóm từ phải sang trái. Tất cả đều yêu cầu một giá trị có thể sửa đổi làm toán hạng bên trái của chúng và trả về một giá trị tham chiếu đến toán hạng bên trái.

Đoạn tương tự tiếp tục nói rằng

Trong mọi trường hợp, phép gán được sắp xếp theo trình tự sau khi tính toán giá trị của các toán hạng bên phải và bên trái, và trước khi tính toán giá trị của biểu thức gán.

Điều này cho thấy rằng trong C ++ 11, biểu thức không còn có hành vi không xác định nữa.


3
Vâng, nó chắc chắn là UB vì các điểm trình tự. Nó cũng là mã không hợp lệ trong C (nhưng không phải C ++) nhưng không liên quan đến các điểm trình tự và sẽ bị trình biên dịch bắt.
Konrad Rudolph

2
@KonradRudolph: không, trình biên dịch không có nghĩa vụ phải bắt hành vi không xác định, trái ngược với mã không hợp lệ. Phần "nên được trình biên dịch bắt" là nơi chúng tôi không đồng ý.

2
Các điểm trình tự không tồn tại trong C ++ 11, vì vậy lý do thực sự cho UB là có hai sửa đổi đối với iđiều đó là không có kết quả.
Mankarse

4
Sai vì đây là hành vi không xác định. Nếu phép gán không theo trình tự trước khi tính toán giá trị của biểu thức gán thì i = j+=1sẽ dẫn đến một giá trị không xác định. Từ cùng một đoạn, bạn trích dẫn "Trong mọi trường hợp, phép gán được sắp xếp theo trình tự sau khi tính toán giá trị của các toán hạng bên phải và bên trái, và trước khi tính toán giá trị của biểu thức gán." Do đó (i+=10)+=10được xác định rõ để làm i += 10; i += 10;. Mặt khác (i+=10)+=(i+=10)là UB.
bames53

2
Vì tôi thấy rằng có một số bất đồng về điều này giữa những người bình luận, tôi đã đăng câu hỏi này dưới dạng một câu hỏi riêng: stackoverflow.com/questions/10655290/…
NPE
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.