Với i = 0, tại sao (i + = i ++) bằng 0?


253

Lấy mã sau đây (có thể sử dụng làm Ứng dụng Bảng điều khiển):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

Kết quả ilà 0. Tôi mong đợi 2 (như một số đồng nghiệp của tôi đã làm). Có lẽ trình biên dịch tạo ra một số loại cấu trúc dẫn đến ikhông.

Lý do tôi mong đợi 2 là, trong dòng suy nghĩ của tôi, câu lệnh bên tay phải sẽ được đánh giá đầu tiên, tăng i với 1. Hơn nó được thêm vào i. Vì tôi đã là 1, nên nó thêm 1 đến 1. Vì vậy, 1 + 1 = 2. Rõ ràng đây không phải là điều đang xảy ra.

Bạn có thể giải thích những gì trình biên dịch làm hoặc những gì xảy ra trong thời gian chạy? Tại sao kết quả bằng không?

Một số loại từ chối trách nhiệm: Tôi hoàn toàn biết rằng bạn sẽ không (và có lẽ không nên) sử dụng mã này. Tôi biết tôi sẽ không bao giờ Tuy nhiên, tôi thấy thật thú vị khi biết lý do tại sao nó hoạt động theo cách như vậy và những gì đang xảy ra chính xác.


57
không nên kết quả mong đợi là 1? i (0) + = i ++ (1) do đó 0 + = 1 = 1
aleation

11
Nó hoạt động như mong đợi
Steve's a D

177
Có bao nhiêu biến thể của câu hỏi này sẽ được hỏi?
mowwwalker

20
Preincrement sẽ tăng giá trị trước khi thực hiện cáo buộc i + = ++ tôi sẽ cung cấp cho bạn 1
Pierluc SS

21
Tại sao mọi người tập trung vào trước và sau khi sinh? Điều "lạ" là giá trị của iphía bên trái +=được "lưu trữ" trước khi phía bên phải được đánh giá. Điều này là phản trực giác, ví dụ, nó sẽ yêu cầu một hoạt động sao chép nếu ilà một đối tượng. (Xin đừng hiểu lầm tôi: Tôi hoàn toàn đồng ý cho rằng đó 0là câu trả lời đúng và phù hợp với tiêu chuẩn.)
JohnB

Câu trả lời:


425

Điều này:

int i = 0;
i += i++

Có thể được nhìn thấy như bạn đang làm (sau đây là một sự đơn giản hóa quá mức):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Điều gì thực sự xảy ra có liên quan nhiều hơn thế - hãy xem MSDN, 7.5.9 Toán tử tăng và giảm mã hậu tố :

Quá trình xử lý thời gian chạy của hoạt động tăng hoặc giảm hậu tố của biểu mẫu x ++ hoặc x-- bao gồm các bước sau:

  • Nếu x được phân loại là một biến:

    • x được ước tính để tạo ra biến.
    • Giá trị của x được lưu.
    • Toán tử đã chọn được gọi với giá trị lưu của x làm đối số của nó.
    • Giá trị được trả về bởi toán tử được lưu trữ ở vị trí được đánh giá bởi x.
    • Giá trị lưu của x trở thành kết quả của hoạt động.

Lưu ý rằng do thứ tự ưu tiên , hậu tố ++xảy ra trước đó += , nhưng kết quả cuối cùng không được sử dụng (vì giá trị trước đó iđược sử dụng).


Một sự phân tách kỹ lưỡng hơn về i += i++các bộ phận được tạo ra đòi hỏi người ta phải biết rằng cả hai +=++không phải là nguyên tử (nghĩa là, không ai là một hoạt động đơn lẻ), ngay cả khi chúng trông giống như chúng. Cách thức thực hiện này bao gồm các biến tạm thời, các bản sao itrước khi các hoạt động diễn ra - một cho mỗi hoạt động. (Tôi sẽ sử dụng tên iAddiAssigncho các biến tạm thời được sử dụng cho +++=tương ứng).

Vì vậy, một xấp xỉ gần hơn với những gì đang xảy ra sẽ là:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

3
@Oded Thao ++tác được thực hiện trước khi đánh giá câu lệnh hoàn tất. Vì vậy, +=ghi đè lên giá trị. Đây có phải là những gì đã xảy ra?
Anirudh Ramanathan

6
@Oded thực sự của nó : int i = 0; i = i + 1; (postfix) i = 0; (assignment). Nếu bạn đã sử dụng i ở nơi khác trong câu lệnh đó, nó sẽ đánh giá là 1 tại thời điểm đó.
drch

@Cthulhu - Về cơ bản. Câu trả lời của dtb đi vào chi tiết.
Oded

6
Tôi không mua cái này. Câu trả lời của @yoriy chính xác hơn nhiều. Đối với một, trong câu trả lời của bạn, bạn nói rằng dòng cuối cùng sẽ là i+1trong khi nó nên được i=i+1. Đó không phải là cái gì i++sao?
tái hiện

3
Phần đầu tiên của câu trả lời là dư thừa. Mẫu mã cuối cùng của bạn đã có thể thực hiện nó IMHO. +1 mặc dù.
corazza

194

Tháo gỡ mã chạy:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Mã tương đương

Nó biên dịch thành mã giống như mã sau:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Việc tháo gỡ mã thứ hai (chỉ để chứng minh chúng giống nhau)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Mở cửa sổ tháo gỡ

Hầu hết mọi người không biết, hoặc thậm chí không nhớ, rằng họ có thể thấy mã lắp ráp trong bộ nhớ cuối cùng, sử dụng cửa sổ Tháo gỡ Visual Studio . Nó cho thấy mã máy đang được thực thi, nó không phải là CIL.

Sử dụng cái này trong khi gỡ lỗi:

Debug (menu) -> Windows (submenu) -> Disassembly

Vì vậy, những gì đang xảy ra với postfix ++?

Postfix ++ nói rằng chúng tôi muốn tăng giá trị của toán hạng sau khi đánh giá ... mọi người đều biết ... điều gây nhầm lẫn một chút là ý nghĩa của "sau khi đánh giá" .

Vậy "sau khi đánh giá" nghĩa là gì:

  • các cách sử dụng khác của toán hạng, trên cùng một dòng mã phải bị ảnh hưởng:
    • a = i++ + i thứ hai tôi bị ảnh hưởng bởi sự gia tăng
    • Func(i++, i) thứ hai tôi bị ảnh hưởng
  • các cách sử dụng khác trên cùng dòng tôn trọng toán tử ngắn mạch như ||&&:
    • (false && i++ != i) || i == 0 thứ ba tôi không bị ảnh hưởng bởi i ++ vì nó không được đánh giá

Vậy ý nghĩa của : i += i++;?

Nó giống như i = i + i++;

Thứ tự đánh giá là:

  1. Lưu trữ i + i (đó là 0 + 0)
  2. Tăng i (tôi trở thành 1)
  3. Gán giá trị của bước 1 cho i (i trở thành 0)

Không phải là sự gia tăng đang bị loại bỏ.

Ý nghĩa của : i = i++ + i;?

Điều này không giống như ví dụ trước. Thứ 3 ibị ảnh hưởng bởi sự gia tăng.

Thứ tự đánh giá là:

  1. Lưu trữ i (đó là 0)
  2. Tăng i (tôi trở thành 1)
  3. Lưu trữ giá trị của bước 1 + i (đó là 0 + 1)
  4. Gán giá trị của bước 3 cho i (i trở thành 1)

22
+ 1 ++ - cho mổ xẻ khó khăn. Chuck Norris sẽ tự hào :) Tôi đoán bạn có thể đưa ra giả định rằng OP đang ở trên Intel, không phải là cổng Mono ...
StuartLC

19
C # có một thứ tự đánh giá được xác định rõ cho biểu thức và mã đối tượng chỉ thực hiện thứ tự đó. Đầu ra mã máy không phải là lý do hoặc giải thích cho thứ tự đánh giá.
Kaz

8
Mã máy giúp dễ hiểu cách thức thực hiện đánh giá IMO.
Kevin

5
@StuartLC Tôi thấy những gì bạn đã làm ở đó. Xấu hổ về upvote bị loại bỏ mặc dù.
Donffan Donal

2
a++ + akhông giống như a + a++bởi vì đây không còn là toán học thuần túy. Luật giao hoán trong đại số không tính đến khả năng các biến thay đổi giá trị giữa chừng thông qua một biểu thức. Toán học chỉ ánh xạ gọn gàng để lập trình khi lập trình là lập trình chức năng. Và thậm chí sau đó, vì những hạn chế đại diện. Ví dụ, số dấu phẩy động đôi khi hoạt động như thực và đôi khi không. Ngay cả khi không có tác dụng phụ, luật giao hoán và kết hợp áp dụng cho các số thực trong toán học vượt qua các số có dấu phẩy động.
Kaz

61
int i = 0;
i += i++;

được đánh giá như sau:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

tức ilà được thay đổi hai lần: một lần bởi i++biểu thức và một lần bởi+= câu lệnh.

Nhưng các toán hạng của +=câu lệnh là

  • giá trị itrước khi đánh giá i++(phía bên trái của+= ) và
  • giá trị itrước khi đánh giá i++(bên phải của +=).

Ah đây là một lời giải thích tuyệt vời. Nhắc nhở tôi khi tôi làm việc trên một máy tính dựa trên ngăn xếp bằng cách sử dụng ký hiệu đánh bóng ngược.
Nathan

36

Đầu tiên, i++trả về 0. Sau đói được tăng thêm 1. Cuối cùng iđược đặt thành giá trị ban đầu ilà 0 cộng với giá trị được i++trả về, cũng bằng không. 0 + 0 = 0.


2
Nhưng i += i++;không i = i++;, vì vậy, giá trị của i++(0) được thêm vào ivà không " iđược đặt thành giá trị được i++trả về". Bây giờ câu hỏi là, khi thêm giá trị được i++trả về i, sẽ ilà giá trị tăng hay giá trị không tăng? Câu trả lời, bạn của tôi, được viết trong thông số kỹ thuật.
Daniel Fischer

Đúng, tôi sẽ sửa nó. Nhưng dù sao kể từ i = 0ban đầu, i += somethingtương đương với i = 0 + somethingđó là i = something.
Jong

32

Điều này chỉ đơn giản là từ trái sang phải, đánh giá từ dưới lên của cây cú pháp trừu tượng. Về mặt khái niệm, cây của biểu thức được đi từ trên xuống, nhưng đánh giá mở ra khi đệ quy bật lên cây từ dưới lên.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

Đánh giá bắt đầu bằng cách xem xét nút gốc +=. Đó là thành phần chính của biểu thức. Toán hạng bên trái +=phải được ước tính để xác định nơi chúng ta lưu trữ biến và để có được giá trị trước đó bằng không. Tiếp theo, bên phải phải được đánh giá.

Phía bên phải là một ++toán tử tăng sau . Nó có một toán hạng, iđược đánh giá vừa là nguồn của một giá trị, vừa là nơi lưu trữ giá trị. Nhà điều hành đánh giá i, tìm kiếm 0và do đó lưu trữ một 1vị trí đó. Nó trả về giá trị trước,0 phù hợp với ngữ nghĩa của nó là trả về giá trị trước đó.

Bây giờ kiểm soát đã trở lại cho các +=nhà điều hành. Bây giờ nó có tất cả các thông tin để hoàn thành hoạt động của nó. Nó biết nơi lưu trữ kết quả (vị trí lưu trữ i) cũng như giá trị trước đó và nó có giá trị được thêm vào giá trị trước đó, cụ thể là 0. Vì vậy, ikết thúc bằng không.

Giống như Java, C # đã vệ sinh một khía cạnh rất thông minh của ngôn ngữ C bằng cách sửa thứ tự đánh giá. Từ trái sang phải, từ dưới lên: thứ tự rõ ràng nhất có khả năng được các lập trình viên mong đợi.


+1: Tôi đồng ý với bạn, ngoại trừ việc mọi lập trình viên đều mong đợi rằng ... Tôi mong đợi nó giống như thế này: SetSum(ref i, Inc(ref i))với int SetSum(ref int a, int b) { return a += b; }int Inc(ref int a) { return a++; }... tất nhiên tôi không còn mong đợi điều đó nữa.
Miguel Angelo

Ngoài ra, những gì tôi mong đợi là không nhất quán! Nó sẽ không bằng Set(ref i, Sum(i, Inc(ref i)))với int Set(ref int a, int b) { return a = b; }int Sum(int a, int b) { return a + b; }.
Miguel Angelo

Cảm ơn; bạn gợi ý về một lỗ hổng / không đầy đủ trong câu trả lời của tôi mà tôi phải sửa.
Kaz

Vấn đề với SetSumlà nó không đánh giá toán hạng bên trái i, mà chỉ lấy địa chỉ của nó, vì vậy nó không tương đương với đánh giá hoàn toàn từ trái sang phải của toán hạng. Bạn cần một cái gì đó như SetSum(ref i, i, PostInc(ref i)). Đối số thứ hai SetSumlà giá trị được thêm vào, trong đó chúng ta chỉ sử dụng iđể chỉ định giá trị trước của i. SetSumchỉ là int SetSum(ref int dest, int a, int b) { return dest = a + b; }.
Kaz

Sự nhầm lẫn xảy ra (ít nhất là đối với tôi) với toán tử + =, bởi vì toán tử gán có đánh giá từ phải sang trái (ví dụ a = b = c = d) ... vì vậy người ta có thể tưởng tượng rằng + = tuân theo quy tắc tương tự, như một hoạt động nguyên tử (như tôi đã làm với phương pháp SetSum của mình) ... nhưng thực tế điều đó xảy ra là C # chuyển a += bthành a = a + b... cho thấy toán tử + = không phải là nguyên tử ... nó chỉ là đường cú pháp.
Miguel Angelo

30

Bởi vì i++đầu tiên trả về giá trị, sau đó tăng nó. Nhưng sau khi tôi được đặt thành 1, bạn đặt lại về 0.


17

Phương pháp tăng sau trông giống như thế này

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

Vì vậy, về cơ bản khi bạn gọi i++, ilà tăng nhưng giá trị ban đầu được trả về trong trường hợp của bạn, nó được trả về 0.


12

Câu trả lời đơn giản

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;

12

i ++ có nghĩa là: trả về giá trị của i THEN tăng nó.

i + = i ++ có nghĩa là: Lấy giá trị hiện tại của i. Thêm kết quả của i ++.

Bây giờ, hãy thêm i = 0 làm điều kiện bắt đầu. i + = i ++ hiện được đánh giá như thế này:

  1. Giá trị hiện tại của tôi là gì? Nó là 0. Lưu trữ nó để chúng ta có thể thêm kết quả của i ++ vào nó.
  2. Đánh giá i ++ (ước tính bằng 0 vì đó là giá trị hiện tại của i)
  3. Tải giá trị được lưu trữ và thêm kết quả của bước 2 vào nó. (thêm 0 đến 0)

Lưu ý: Ở cuối bước 2, giá trị của i thực sự là 1. Tuy nhiên, trong bước 3, bạn loại bỏ nó bằng cách tải giá trị của i trước khi nó được tăng lên.

Trái ngược với i ++, ++ i trả về giá trị tăng dần.

Do đó, i + = ++ tôi sẽ cung cấp cho bạn 1.


Đó là sự giúp đỡ Full
sonsha

11

Toán tử gia tăng sửa chữa bài đăng ++, cung cấp cho biến một giá trị trong biểu thức và sau đó thực hiện giá trị gia tăng mà bạn đã gán trả về giá trị 0 (0) cho imột lần nữa ghi đè lên giá trị tăng (1) , do đó bạn sẽ nhận được số không. Bạn có thể đọc thêm về toán tử gia tăng trong Toán tử ++ (MSDN).


8

i += i++;sẽ bằng không, bởi vì nó làm ++sau đó.

i += ++i; sẽ làm điều đó trước


4
Nếu nó xảy ra ++sau đó, tôi mong đợi kết quả 1.
comecme

8

Tiền tố ++ đánh giá itrước khi tăng nó và +=chỉ đánh giá imột lần.

Do đó, 0 + 0 = 0, như iđược ước tính và sử dụng trước khi nó được tăng lên, như định dạng postfix của ++được sử dụng. Để được ităng lên trước tiên, hãy sử dụng mẫu tiền tố ( ++i).

(Ngoài ra, chỉ cần một lưu ý: bạn chỉ nên nhận 1, vì 0 + (0 + 1) = 1)

Tài liệu tham khảo: http://msdn.microsoft.com/en-us/l Library / sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/l Library / x43w8w.aspx (++)


8

C # đang làm gì và "tại sao" của sự nhầm lẫn

Tôi cũng mong đợi giá trị là 1 ... nhưng một số thăm dò về vấn đề đó đã làm rõ một số điểm.

Cosider các phương pháp sau:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

Tôi dự kiến ​​sẽ i += i++giống như SetSum(ref i, Inc(ref i)). Giá trị của i sau tuyên bố này là 1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

Nhưng sau đó tôi đi đến một kết luận khác ... i += i++thực sự giống như i = i + i++... vì vậy tôi đã tạo ra một ví dụ tương tự khác, sử dụng các hàm này:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

Sau khi gọi đây Set(ref i, Sum(i, Inc(ref i)))là giá trị của i là 0 :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

Điều này không chỉ giải thích những gì C # đang làm ... mà còn tại sao rất nhiều người nhầm lẫn với nó ... bao gồm cả tôi.


2
Vui lòng thêm câu này vào câu trả lời ban đầu của bạn, nó không thêm bất kỳ lợi ích nào để có nó như một câu trả lời riêng biệt.
casperOne

2
Tôi đã làm điều này để không đưa ra câu trả lời khác, vì đó là về mã dịch ngược ... trong khi ở phần này, tôi đã thử một cách tiếp cận khác để giải thích mọi thứ. Bạn nghĩ sao? Tôi có nên chỉnh sửa câu trả lời khác và nối thêm câu này không? Có lẽ, hãy chuẩn bị cái này ... không biết! Cảm ơn những lời đề nghị!
Miguel Angelo

7

Một kỷ niệm tốt tôi luôn nhớ về điều này là như sau:

Nếu ++đứng sau biểu thức, nó trả về giá trị trước đó . Vậy đoạn mã sau

int a = 1;
int b = a++;

là 1, bởi vì alà 1 trước khi nó tăng lên khi ++đứng sau a . Mọi người gọi đây là bài sửa chữa ký hiệu. Ngoài ra còn có một ký hiệu sửa lỗi trước , trong đó mọi thứ hoàn toàn ngược lại: nếu ++đứng trước , biểu thức trả về giá trị mà nó nằm sau thao tác:

int a = 1;
int b = ++a;

b là hai ở đây.

Vì vậy, đối với mã của bạn, điều này có nghĩa là

int i = 0;
i += (i++);

i++trả về 0 (như mô tả ở trên), vì vậy 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyers mô tả sự khác biệt giữa hai ký hiệu đó trong "Lập trình C ++ hiệu quả". Trong nội bộ, i++(postfix) ghi nhớ giá trị ilà và gọi ký hiệu tiền tố ( ++i) và trả về giá trị cũ , i. Đây là lý do tại sao bạn allways nên sử dụng ++itrong forvòng (mặc dù tôi nghĩ rằng tất cả các trình biên dịch hiện đại đang dịch i++để ++itrong forvòng).


1
Tôi đã thử nghiệm int i = 0; i += (++i), và iđược đặt thành một chứ không phải hai. Nó cũng có ý nghĩa với tôi, vì việc sử dụng tiền tố thay vì tiền tố không thay đổi thực tế rằng, nếu bạn viết i += (++i)ra i = i + (++i), thì iđược đánh giá trước ++i, dẫn đến i = 0 + (++i)và cuối cùng i = 0 + 1.
Wutz

6

Câu trả lời duy nhất cho câu hỏi của bạn là chính xác là: Bởi vì nó không được xác định.

Ok, trước khi tất cả các bạn đốt cháy tôi ..

Tất cả các bạn đã trả lời tại sao i+=i++là ok và hợp lý để dẫn đếni=0 .

Tôi đã cố gắng bỏ phiếu cho mỗi câu trả lời của bạn nhưng danh tiếng mà tôi tính được sẽ quá cao ..

Tại sao tôi rất giận bạn? không phải vì những gì câu trả lời của bạn giải thích ..
Ý tôi là, mọi câu trả lời tôi đọc đều có nỗ lực vượt trội để giải thích điều không thể, tôi Vỗ tay!

Nhưng kết quả là gì ?? Là kết quả trực quan - nó có phải là kết quả chấp nhận được không ??

Mỗi người trong số các bạn đã nhìn thấy "vị vua trần trụi" và bằng cách nào đó đã chấp nhận nó như một vị vua hợp lý.

Bạn là tất cả SAU!

i+=i++;kết quả 0là không xác định.

một lỗi trong cơ chế đánh giá ngôn ngữ nếu bạn sẽ .. hoặc thậm chí tệ hơn! một lỗi trong thiết kế.

muốn một bằng chứng? tất nhiên bạn muốn

int t=0; int i=0; t+=i++; //t=0; i=1

Bây giờ đây ... là kết quả trực quan! bởi vì lần đầu tiên chúng tôi đánh giá tđược gán nó với một giá trị và chỉ sau khi đánh giá và chuyển nhượng, chúng tôi mới có hoạt động đăng bài xảy ra - có hợp lý không?

nó có hợp lý không: i=i++i=imang lại kết quả tương tự cho i?

trong khi t=i++t=icó kết quả khác nhau cho i.

Các hoạt động bài là một cái gì đó nên xảy ra sau khi đánh giá tuyên bố.
Vì thế:

int i=0;
i+=i++;

Nên giống nhau nếu chúng ta viết:

int i=0;
i = i + i ++;

và do đó giống như:

int i=0;
i= i + i;
i ++;

và do đó giống như:

int i=0;
i = i + i;
i = i + 1;

Bất kỳ kết quả nào không 1chỉ ra lỗi trong trình biên dịch hoặc lỗi trong thiết kế ngôn ngữ nếu chúng ta suy nghĩ hợp lý - tuy nhiên MSDN và nhiều nguồn khác cho chúng ta biết "này - điều này không xác định!"

Bây giờ, trước khi tôi tiếp tục, ngay cả tập hợp các ví dụ tôi đưa ra cũng không được ai ủng hộ hay thừa nhận .. Tuy nhiên đây là những gì theo cách trực quan và hợp lý nên có kết quả.

Các lập trình viên nên không có kiến ​​thức về cách lắp ráp được viết hoặc dịch!

Nếu nó được viết theo cách không tôn trọng các định nghĩa ngôn ngữ - đó là một lỗi!

Và để kết thúc, tôi đã sao chép nó từ Wikipedia, toán tử tăng và giảm :
Do toán tử tăng / giảm điều chỉnh toán hạng của nó, sử dụng toán hạng đó nhiều lần trong cùng một biểu thức có thể tạo ra kết quả không xác định . Ví dụ, trong các biểu thức, chẳng hạn như x - ++ x, không rõ ràng về trình tự phép trừ và toán tử gia tăng nào. Các tình huống như thế này thậm chí còn tồi tệ hơn khi trình tối ưu hóa được áp dụng bởi trình biên dịch, điều này có thể dẫn đến thứ tự thực hiện các hoạt động khác với những gì lập trình viên dự định.

Và do đó.

Câu trả lời đúng là điều này KHÔNG NÊN SỬ DỤNG! (vì nó được HIỂU!)

Có .. - Nó có kết quả không thể đoán trước ngay cả khi trình biên dịch C # đang cố gắng bình thường hóa bằng cách nào đó.

Tôi không tìm thấy bất kỳ tài liệu nào về C # mô tả hành vi mà tất cả các bạn ghi lại là hành vi bình thường hoặc được xác định rõ ràng của ngôn ngữ. Những gì tôi đã tìm thấy là hoàn toàn ngược lại!

[ được sao chép từ tài liệu MSDN cho Toán tử tăng và giảm Postfix: ++ và - ]

Khi một toán tử postfix được áp dụng cho một đối số hàm, giá trị của đối số không được đảm bảo được tăng hoặc giảm trước khi nó được truyền cho hàm. Xem phần 1.9.17 trong tiêu chuẩn C ++ để biết thêm thông tin.

Lưu ý rằng những từ không được bảo đảm ...

Hãy tha thứ cho tôi nếu câu trả lời đó có vẻ kiêu ngạo - tôi không phải là người kiêu ngạo. Tôi chỉ xem xét rằng hàng ngàn người đến đây để tìm hiểu và những câu trả lời tôi đọc sẽ đánh lừa họ và sẽ làm hại logic và sự hiểu biết của họ về chủ đề này.


Tôi không chắc chắn tôi đang theo dõi 100%, nhưng bạn tham khảo tài liệu C ++, nhưng câu hỏi của tôi là về C #. Các tài liệu về đó là ở đây .
Peter

Tôi đã đề cập đến C # trong câu trả lời của tôi. Từ liên kết bạn đã cung cấp: Kết quả của x ++ hoặc x-- là giá trị của x trước hoạt động, trong khi kết quả của ++ x hoặc --x là giá trị của x sau hoạt động. Trong cả hai trường hợp, x chính nó có cùng giá trị sau khi hoạt động. rõ ràng cho thấy đây không phải là trường hợp khi thử nghiệm .. bởi vì i=++isẽ cung cấp kết quả khác nhau từ i=i++. Vì vậy, câu trả lời của tôi đứng.
GY

Aha, ok, nhưng thật khó hiểu khi bạn tham khảo tài liệu C ++. Vì vậy, những gì bạn đang nói là đặc điểm kỹ thuật đã không được thực hiện chính xác?
Peter

Không. Điều tôi đang nói là nó không được xác định theo đặc điểm kỹ thuật và việc sử dụng không xác định sẽ dẫn đến kết quả không xác định.
GY

Không xác định trong C ++, nhưng C # nói rằng nó phải có cùng giá trị sau khi hoạt động , phải không? Điều đó không giống như không xác định (nhưng tôi đồng ý rằng bạn không nên sử dụng nó, xem từ chối trách nhiệm của tôi, tôi chỉ cố gắng hiểu những gì đang xảy ra).
Peter

4

Toán tử ++ sau biến làm cho nó tăng hậu tố. Sự gia tăng xảy ra sau khi mọi thứ khác trong câu lệnh, thêm và gán. Nếu thay vào đó, bạn đặt ++ trước biến, nó sẽ xảy ra trước khi giá trị của i được đánh giá và đưa ra câu trả lời mong đợi.


2
Các ++không xảy ra sau các +=tuyên bố, nó sẽ xảy ra trong quá trình thực hiện các +=tuyên bố. Đó là lý do tại sao các hiệu ứng của ++ghi đè bằng +=.
dtb

Sử dụng ++ tôi thực sự có kết quả là 1, không phải trong 2 ('câu trả lời dự kiến' ban đầu của tôi).
Peter

Có vẻ như bài tập += ghi đè sửa đổi do tăng trước hoặc sau trong biểu thức.
Steven Lu

4

Các bước trong tính toán là:

  1. int i=0 // Khởi tạo thành 0
  2. i+=i++ // Phương trình
  3. i=i+i++ // sau khi đơn giản hóa phương trình bằng trình biên dịch
  4. i=0+i++ // i thay thế giá trị
  5. i=0+0 // i ++ là 0 như được giải thích dưới đây
  6. i=0 // Kết quả cuối cùng i = 0

Ở đây, ban đầu giá trị ilà 0. WKT, i++không có gì ngoài: đầu tiên sử dụng igiá trị và sau đó tăng igiá trị lên 1. Vì vậy, nó sử dụng igiá trị 0, trong khi tính toán i++và sau đó tăng nó lên 1. Vì vậy, nó dẫn đến một giá trị bằng 0.


3

Có hai lựa chọn:

Tùy chọn đầu tiên: nếu trình biên dịch đọc câu lệnh như sau,

i++;
i+=i;

thì kết quả là 2.

Dành cho

else if
i+=0;
i++;

kết quả là 1.


5
Không phải trong số đó là kết quả thực tế .
Steven Lu

3

Hãy cẩn thận: đọc Câu hỏi thường gặp về C : những gì bạn đang cố gắng thực hiện (trộn bài tập và ++cùng một biến) không chỉ không được chỉ định mà còn không được xác định (có nghĩa là trình biên dịch có thể làm bất cứ điều gì khi đánh giá!, Không chỉ đưa ra kết quả "hợp lý").

Xin vui lòng đọc, phần 3 . Toàn bộ phần này cũng đáng để đọc! Đặc biệt là 3.9, giải thích hàm ý của không xác định. Mục 3.3 cung cấp cho bạn một bản tóm tắt nhanh chóng về những gì bạn có thể và không thể làm với "i ++" và những thứ tương tự.

Tùy thuộc vào trình biên dịch nội bộ, bạn có thể nhận được 0, hoặc 2 hoặc 1 hoặc thậm chí bất kỳ thứ gì khác! Và vì nó không được xác định, nên họ đồng ý làm như vậy.


Rất tiếc, c # ... Tôi đã bị "gcc" ném đi, một số người đã đi qua để tháo rời mã.
Olivier Dulac

1
Tôi đã bỏ lỡ nó là C #, nhưng dù sao cũng thích câu trả lời.
Iain Collins

1
@Iain: cảm ơn, tôi cũng tin rằng nó đáng để giữ câu trả lời có sẵn, nhiều người không biết về điều này (hoặc về faq tuyệt vời đó , từ thời điểm tốt nhất của Usenet, nơi hầu hết những người có kiến ​​thức về một subjcet sẽ giống nhau nơi để cập nhật nó)
Olivier Dulac

3

Có rất nhiều lý do tuyệt vời trong các câu trả lời ở trên, tôi vừa làm một bài kiểm tra nhỏ và muốn chia sẻ với bạn

int i = 0;
i+ = i++;

Ở đây kết quả tôi đang hiển thị 0 kết quả. Bây giờ hãy xem xét các trường hợp dưới đây:

Trường hợp 1:

i = i++ + i; //Answer 1

trước đó tôi nghĩ mã ở trên giống với mã này nên thoạt nhìn câu trả lời là 1, và câu trả lời thực sự của tôi cho cái này là 1.

Trường hợp 2:

i = i + i++; //Answer 0 this resembles the question code.

ở đây toán tử gia tăng không đi vào đường dẫn thực thi, không giống như trường hợp trước đó trong đó i ++ có cơ hội thực thi trước khi bổ sung.

Tôi hy vọng điều này sẽ giúp một chút. Cảm ơn


2

Hy vọng sẽ trả lời điều này từ một loại phối cảnh 101 lập trình C.

Có vẻ như tôi đang xảy ra theo thứ tự này:

  1. iđược đánh giá là 0, dẫn đến i = 0 + 0hoạt động gia tăng i++"xếp hàng", nhưng việc gán 0 thành ichưa xảy ra.
  2. Sự gia tăng i++xảy ra
  3. Nhiệm vụ i = 0từ phía trên xảy ra, ghi đè hiệu quả bất cứ điều gì # 2 (phần tăng sau) sẽ thực hiện.

Bây giờ, # 2 có thể không bao giờ thực sự xảy ra (có thể không?) Bởi vì trình biên dịch có thể nhận ra nó sẽ không phục vụ mục đích nào, nhưng điều này có thể phụ thuộc vào trình biên dịch. Dù bằng cách nào, các câu trả lời khác, có nhiều kiến ​​thức hơn đã chỉ ra rằng kết quả là chính xác và phù hợp với tiêu chuẩn C #, nhưng không xác định điều gì xảy ra ở đây cho C / C ++.

Làm thế nào và tại sao vượt quá chuyên môn của tôi, nhưng thực tế là việc chuyển nhượng bên phải được đánh giá trước đó xảy ra sau khi tăng sau có lẽ là điều khó hiểu ở đây.

Hơn nữa, bạn sẽ không mong đợi kết quả là 2 bất kể trừ khi bạn làm ++ithay vì i++tôi tin.


1
Phiên bản tiền sản xuất tạo ra kết quả 2với C ++: ideone.com/8dH8tf
Steven Lu

Điều đó có ý nghĩa. Nhưng mức tăng trước là một tình huống ít phức tạp hơn so với mức tăng sau.
gkimsey

2

Chỉ cần đặt,

i ++, sẽ thêm 1 vào "i" sau khi toán tử "+ =" hoàn thành.

Những gì bạn muốn là ++ i, để nó sẽ thêm 1 vào "i" trước khi toán tử "+ =" được thực thi.


0
i=0

i+=i

i=i+1

i=0;

Sau đó, 1 được thêm vào i.

i + = i ++

Vì vậy, trước khi thêm 1 vào i, ilấy giá trị bằng 0. Chỉ khi chúng ta thêm 1 trước, ihãy lấy giá trị 0.

i+=++i

i=2

-4

Câu trả lời isẽ là 1.

Chúng ta hãy xem làm thế nào:

Ban đầu i=0;.

Sau đó, trong khi tính toán i +=i++;theo giá trị của chúng ta sẽ có một cái gì đó như thế 0 +=0++;, vì vậy theo ưu tiên của nhà điều hành 0+=0sẽ thực hiện đầu tiên và kết quả sẽ được 0.

Sau đó, toán tử gia tăng sẽ được áp dụng 0++, 0+1và giá trị của isẽ là 1.


3
Câu trả lời này là sai. Bạn sẽ không nhận được 1 vì khi bạn thực hiện 0 += 0++;chuyển nhượng sau khi tăng ++nhưng với giá trị tôi đã giải thích trước đó ++(vì là nhà điều hành bưu điện.
PhoneixS

2
Xin lỗi, nhưng điều này không chính xác. Đọc câu hỏi của tôi và bạn sẽ thấy tôi nói kết quả là 0. Nếu bạn chạy mã, bạn sẽ thấy nó có hiệu quả 0.
Peter
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.