Tại sao điều này đi vào một vòng lặp vô hạn?


493

Tôi có đoạn mã sau:

public class Tests {
    public static void main(String[] args) throws Exception {
        int x = 0;
        while(x<3) {
            x = x++;
            System.out.println(x);
        }
    }
}

Chúng tôi biết anh ta nên viết chỉ x++hoặc x=x+1, nhưng x = x++trước tiên nó nên thuộc tính xcho chính nó, và sau đó tăng nó. Tại sao xtiếp tục với 0giá trị?

- ngày

Đây là mã byte:

public class Tests extends java.lang.Object{
public Tests();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge   22
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  iload_1
   16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   19:  goto    2
   22:  return

}

Tôi sẽ đọc về các hướng dẫn để cố gắng hiểu ...


8
Tôi nghi ngờ những gì đang xảy ra là: 1. tải x vào một thanh ghi (= 0); 2. tăng x (x = 1); 3. lưu giá trị thanh ghi vào x (x = 0). Trong C / C ++, đây sẽ là hành vi không xác định vì không có điểm thứ tự chính thức để xác định thứ tự 2 và 3. Hy vọng ai đó có thể trích dẫn cho bạn một cái gì đó tương đương từ thông số Java.
Rup

19
Chúng tôi đã thử điều này trong C ++ để xem điều gì sẽ xảy ra và nó in 1,2,3 và thoát. Tôi không trông đợi điều đó. Tôi giả sử nó phụ thuộc vào trình biên dịch, vì nó là hành vi không xác định. Chúng tôi đã sử dụng gnu g ++.
đau buồn

13
@saj x++là tăng sau; x=là sự phân công kết quả ; các kết quả của x++là bản gốc x(và có một tác dụng phụ của tăng, nhưng điều đó không làm thay đổi kết quả), Vì vậy, đây có thể được hiểu làvar tmp = x; x++; x = tmp;
Marc Gravell

5
Bây giờ tôi có một câu hỏi phổ biến Tôi rất tiếc, các câu trả lời (lặp đi lặp lại) không ngừng đến, ngay cả sau khi câu trả lời đúng đã được chọn. Màn hình "Hoạt động gần đây" của tôi có đầy đủ các câu trả lời giống nhau và sắp tới ...
Tom Brito

3
@Rob Vermeulen bạn có thể muốn đọc toàn bộ câu hỏi trước khi đưa ra nhận xét ..;) Đây là một mã được tạo bởi một sinh viên của tôi, và tôi tò mò tại sao lại có hành vi này.
Tom Brito

Câu trả lời:


354

Lưu ý : Ban đầu tôi đã đăng mã C # trong câu trả lời này cho mục đích minh họa, vì C # cho phép bạn truyền inttham số bằng cách tham chiếu với reftừ khóa. Tôi đã quyết định cập nhật nó với mã Java hợp pháp thực tế bằng cách sử dụng MutableIntlớp đầu tiên tôi tìm thấy trên Google để sắp xếp gần đúng những gì reftrong C #. Tôi thực sự không thể biết nếu điều đó giúp hoặc làm tổn thương câu trả lời. Tôi sẽ nói rằng cá nhân tôi đã không thực hiện tất cả sự phát triển Java đó; vì vậy đối với tất cả tôi biết có thể có nhiều cách thành ngữ hơn để minh họa điểm này.


Có lẽ nếu chúng ta viết ra một phương pháp để làm tương đương với những gì x++nó sẽ làm cho điều này rõ ràng hơn.

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

Đúng? Tăng giá trị được thông qua và trả về giá trị ban đầu: đó là định nghĩa của toán tử postrement.

Bây giờ, hãy xem cách hành vi này diễn ra trong mã ví dụ của bạn:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)Làm gì? Tăng x, vâng. Và sau đó trả về những gì x trước khi tăng . Giá trị trả về này sau đó được gán cho x.

Vì vậy, thứ tự của các giá trị được gán xlà 0, rồi 1, rồi 0.

Điều này có thể rõ ràng hơn nếu chúng ta viết lại ở trên:

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

Sự cố định của bạn về thực tế là khi bạn thay thế xở phía bên trái của bài tập trên y, "bạn có thể thấy rằng nó đầu tiên tăng x, và sau đó quy nó thành y" khiến tôi bối rối. Nó không phải xlà được giao cho y; nó là giá trị trước đây được gán chox . Thực sự, tiêm ykhiến mọi thứ không khác với kịch bản trên; chúng tôi chỉ đơn giản là có:

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

Vì vậy, rõ ràng: x = x++hiệu quả không thay đổi giá trị của x. Nó luôn khiến x có các giá trị x 0 , sau đó x 0 + 1 và sau đó x 0 lần nữa.


Cập nhật : Ngẫu nhiên, e rằng bạn nghi ngờ rằng xđã từng được gán cho 1 "giữa" thao tác tăng và phép gán trong ví dụ trên, tôi đã đưa ra một bản demo nhanh để minh họa rằng giá trị trung gian này thực sự "tồn tại", mặc dù nó sẽ không bao giờ được "nhìn thấy" trên luồng thực thi.

Bản demo gọi x = x++;trong một vòng lặp trong khi một luồng riêng biệt liên tục in giá trị của xbàn điều khiển.

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

Dưới đây là một đoạn trích của đầu ra của chương trình trên. Lưu ý sự xuất hiện bất thường của cả 1 và 0.

Bắt đầu chủ đề nền ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1

1
Bạn không cần phải tạo một lớp để chuyển qua tham chiếu trong java (mặc dù điều đó chắc chắn sẽ hoạt động). Bạn có thể sử dụng Integerlớp, một phần của thư viện chuẩn và thậm chí nó còn có lợi ích là được tự động đóng hộp đến và từ int gần như trong suốt.
rmeador

3
@rmeador Integer là bất biến, vì vậy bạn vẫn không thể thay đổi giá trị của nó. Tuy nhiên, AtomicInteger có thể thay đổi.
ILMTitan

5
@Dan: Nhân tiện, xtrong ví dụ cuối cùng của bạn phải được khai báo volatile, nếu không thì đó là hành vi không xác định và thấy 1s là triển khai cụ thể.
axtavt

4
@burkestar: Tôi không nghĩ rằng liên kết này khá phù hợp trong trường hợp này, vì đó là một câu hỏi Java và (trừ khi tôi nhầm) hành vi thực sự không được xác định trong C ++.
Dan Tao

5
@Tom Brito - trong C không xác định ... việc ++ này có thể được thực hiện trước hoặc sau khi chuyển nhượng. Thực tế mà nói, có thể có một trình biên dịch thực hiện tương tự như Java, nhưng bạn sẽ không muốn đặt cược vào nó.
gièm pha

170

x = x++ hoạt động theo cách sau:

  • Đầu tiên nó đánh giá biểu thức x++. Đánh giá biểu thức này tạo ra một giá trị biểu thức (là giá trị của xtrước khi tăng) và gia số x.
  • Sau đó, nó gán giá trị biểu thức cho x, ghi đè giá trị tăng dần.

Vì vậy, chuỗi các sự kiện trông như sau (đó là một mã byte được dịch ngược thực tế, như được tạo bởi javap -c, với các nhận xét của tôi):

   8: iload_1 // Ghi nhớ giá trị hiện tại của x trong ngăn xếp
   9: iinc 1, 1 // Tăng x (không thay đổi ngăn xếp)
   12: istore_1 // Viết giá trị được ghi nhớ từ ngăn xếp vào x

Để so sánh , x = ++x:

   8: iinc 1, 1 // Tăng x
   11: iload_1 // Đẩy giá trị của x lên ngăn xếp
   12: istore_1 // Giá trị pop từ ngăn xếp đến x

nếu bạn thực hiện một bài kiểm tra, bạn có thể thấy rằng nó tăng đầu tiên và các thuộc tính sau. Vì vậy, nó không nên thuộc tính không.
Tom Brito

2
@Tom đó là vấn đề, mặc dù - bởi vì đây là tất cả một chuỗi duy nhất nó đang thực hiện theo thứ tự không rõ ràng (và có thể không xác định). Bằng cách thử kiểm tra điều này, bạn đang thêm một điểm thứ tự và có hành vi khác nhau.
Rup

Về đầu ra mã byte của bạn: lưu ý rằng iinctăng một biến, nó không tăng giá trị ngăn xếp, cũng không để lại giá trị trên ngăn xếp (không giống như hầu hết các op số học khác). Bạn có thể muốn thêm mã được tạo ra ++xđể so sánh.
Anon

3
@Rep Nó có thể không được định nghĩa trong C hoặc C ++, nhưng trong Java, nó được xác định rõ.
ILMTitan


104

Điều này xảy ra bởi vì giá trị của xkhông được tăng lên chút nào.

x = x++;

tương đương với

int temp = x;
x++;
x = temp;

Giải trình:

Hãy xem mã byte cho thao tác này. Xem xét một lớp mẫu:

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

Bây giờ đang chạy trình phân tách lớp trên này, chúng tôi nhận được:

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

Bây giờ Java VM được xếp chồng dựa trên nghĩa là cho mỗi thao tác, dữ liệu sẽ được đẩy lên ngăn xếp và từ ngăn xếp, dữ liệu sẽ bật ra để thực hiện thao tác. Ngoài ra còn có một cấu trúc dữ liệu khác, điển hình là một mảng để lưu trữ các biến cục bộ. Các biến cục bộ được cung cấp id chỉ là các chỉ mục cho mảng.

Chúng ta hãy nhìn vào các mnemonics trong main()phương pháp:

  • iconst_0: Giá trị không đổi 0 được đẩy vào ngăn xếp.
  • istore_1: Phần tử trên cùng của ngăn xếp được bật ra và được lưu trữ trong biến cục bộ với chỉ mục 1
    x.
  • iload_1: Giá trị tại vị trí 1là giá trị của x0, được đẩy vào ngăn xếp.
  • iinc 1, 1: Giá trị tại vị trí bộ nhớ 1được tăng lên theo 1. Vì vậy, xbây giờ trở thành 1.
  • istore_1: Giá trị ở đầu ngăn xếp được lưu trữ vào vị trí bộ nhớ 1. Điều đó được 0chỉ định để x ghi đè giá trị gia tăng của nó.

Do đó giá trị của xkhông thay đổi dẫn đến vòng lặp vô hạn.


5
Trên thực tế, nó được tăng lên (đó là ý nghĩa của ++), nhưng biến được ghi đè lên sau đó.
Progman

10
int temp = x; x = x + 1; x = temp;tốt hơn hết là không sử dụng tautology trong ví dụ của bạn.
Scott Chamberlain

52
  1. Ký hiệu tiền tố sẽ tăng biến TRƯỚC KHI biểu thức được ước tính.
  2. Ký hiệu postfix sẽ tăng SAU khi đánh giá biểu thức.

Tuy nhiên " =" có quyền ưu tiên toán tử thấp hơn " ++".

Vậy x=x++;nên đánh giá như sau.

  1. x chuẩn bị cho nhiệm vụ (đánh giá)
  2. x tăng lên
  3. Giá trị trước xđược gán cho x.

Đây là câu trả lời tốt nhất. Một số đánh dấu sẽ giúp nó nổi bật hơn một chút.
Justin Force

1
Cái này sai. Nó không phải là ưu tiên. ++có quyền ưu tiên cao hơn =trong C và C ++, nhưng câu lệnh không được xác định trong các ngôn ngữ đó.
Matthew Flaschen

2
Câu hỏi ban đầu là về Java
Jaydee

34

Không có câu trả lời nào khá đúng, vì vậy hãy vào đây:

Khi bạn viết int x = x++, bạn không chỉ định xlà chính nó ở giá trị mới, bạn đang gán xlà giá trị trả về của x++biểu thức. Điều này xảy ra là giá trị ban đầu của x, như được gợi ý trong câu trả lời của Colin Cochrane .

Để giải trí, hãy kiểm tra mã sau:

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

Kết quả sẽ là

0
1

Giá trị trả về của biểu thức là giá trị ban đầu của x, bằng không. Nhưng sau này, khi đọc giá trị của x, chúng tôi nhận được giá trị cập nhật, đó là một giá trị.


Tôi sẽ cố gắng hiểu các dòng mã byte, xem bản cập nhật của tôi, vì vậy nó sẽ rõ ràng .. :)
Tom Brito

Sử dụng println () rất hữu ích cho tôi trong việc hiểu điều này.
ErikE

29

Nó đã được giải thích tốt bởi khác. Tôi chỉ bao gồm các liên kết đến các phần đặc tả Java có liên quan.

x = x ++ là một biểu thức. Java sẽ tuân theo thứ tự đánh giá . Đầu tiên, nó sẽ đánh giá biểu thức x ++, sẽ tăng x và đặt giá trị kết quả thành giá trị trước đó của x . Sau đó, nó sẽ gán kết quả biểu thức cho biến x. Cuối cùng, x trở lại giá trị trước đó.


1
+1. Đây là câu trả lời tốt nhất cho câu hỏi thực tế, "Tại sao?"
Matthew Flaschen

18

Tuyên bố này:

x = x++;

đánh giá như thế này:

  1. Đẩy xlên ngăn xếp;
  2. Tăng x;
  3. Pop xtừ ngăn xếp.

Vì vậy, giá trị là không thay đổi. So sánh với:

x = ++x;

được đánh giá là:

  1. Tăng x;
  2. Đẩy xlên ngăn xếp;
  3. Pop xtừ ngăn xếp.

Những gì bạn muốn là:

while (x < 3) {
  x++;
  System.out.println(x);
}

13
Chắc chắn việc thực hiện đúng, nhưng câu hỏi là "tại sao?".
p.campbell

1
Mã ban đầu đã sử dụng tăng sau trên x và sau đó gán mã cho x. x sẽ bị ràng buộc với x trước khi tăng, do đó nó sẽ không bao giờ thay đổi giá trị.
wkl

5
@cletus Tôi không phải là người xuống, nhưng câu trả lời ban đầu của bạn không chứa lời giải thích. Nó chỉ nói làm 'x ++ `.
Petar Minchev

4
@cletus: Tôi không downvote, nhưng câu trả lời của bạn ban đầu chỉ là x++đoạn mã.
p.campbell

10
Giải thích là không chính xác quá. Nếu mã đầu tiên được gán x cho x và sau đó tăng x, nó sẽ hoạt động tốt. Chỉ cần thay đổi x++;giải pháp của bạn x=x; x++;và bạn đang làm những gì bạn cho là mã gốc đang làm.
Wooble

10

Câu trả lời là khá đơn giản. Nó phải làm với thứ tự được đánh giá. x++trả về giá trị xsau đó tăng dần x.

Do đó, giá trị của biểu thức x++0. Vì vậy, bạn đang chỉ định x=0mỗi lần trong vòng lặp. Chắc chắn x++tăng giá trị này, nhưng điều đó xảy ra trước khi chuyển nhượng.


1
Ồ, có quá nhiều chi tiết trên trang này khi câu trả lời ngắn gọn và đơn giản, tức là câu này.
Charles Goodwin

8

Từ http://doad.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

Các toán tử tăng / giảm có thể được áp dụng trước (tiền tố) hoặc sau (hậu tố) toán hạng. Kết quả mã ++; và kết quả ++; cả hai sẽ kết thúc trong kết quả được tăng lên bởi một. Sự khác biệt duy nhất là phiên bản tiền tố (kết quả ++) ước tính giá trị tăng dần, trong khi phiên bản hậu tố (kết quả ++) ước tính giá trị ban đầu . Nếu bạn chỉ thực hiện một mức tăng / giảm đơn giản, thì việc bạn chọn phiên bản nào thực sự không quan trọng. Nhưng nếu bạn sử dụng toán tử này trong một phần của biểu thức lớn hơn, thì biểu thức bạn chọn có thể tạo ra sự khác biệt đáng kể.

Để minh họa, hãy thử như sau:

    int x = 0;
    int y = 0;
    y = x++;
    System.out.println(x);
    System.out.println(y);

Mà sẽ in 1 và 0.


1
Tuy nhiên, đó không phải là kết quả đánh giá, đó là vấn đề của các cửa hàng.
Rup

2
Tôi không đồng ý. Nếu x = 0 thì x ++ sẽ trả về 0. Do đó x = x ++ sẽ dẫn đến x = 0.
Colin Cochrane

Rup nói đúng về điều này. Đó là thứ tự của các cửa hàng có vấn đề trong trường hợp cụ thể này. y = x ++ không giống với x = x ++; Ở cái sau, x đang được gán 2 giá trị trong cùng một biểu thức. Tay trái x đang được chỉ định kết quả đánh giá biểu thức x ++, là 0. Phía bên phải x đang được tăng lên 1. Theo thứ tự 2 phép gán này xảy ra là vấn đề gì. Từ các bài viết trước, rõ ràng cách thức hoạt động của nó là: eval = x ++ => eval == 0: tăng phải x => x == 1: left x = eval => x == 0
Michael Ekoka

7

Bạn thực sự có được các hành vi sau đây.

  1. lấy giá trị của x (bằng 0) làm "kết quả" của bên phải
  2. tăng giá trị của x (vì vậy x bây giờ là 1)
  3. gán kết quả của phía bên phải (được lưu là 0) cho x (x bây giờ là 0)

Ý tưởng là toán tử tăng sau (x ++) tăng biến đó trong câu hỏi SAU trả về giá trị của nó để sử dụng trong phương trình mà nó được sử dụng trong.

Chỉnh sửa: Thêm một chút vì nhận xét. Hãy xem xét nó như sau.

x = 1;        // x == 1
x = x++ * 5;
              // First, the right hand side of the equation is evaluated.
  ==>  x = 1 * 5;    
              // x == 2 at this point, as it "gave" the equation its value of 1
              // and then gets incremented by 1 to 2.
  ==>  x = 5;
              // And then that RightHandSide value is assigned to 
              // the LeftHandSide variable, leaving x with the value of 5.

OK, nhưng những gì chỉ định thứ tự của bước 2 và 3?
Rup

@Rup - Ngôn ngữ định nghĩa nó. Phía bên phải của phương trình được đánh giá đầu tiên (trong trường hợp này là "x ++") và kết quả được gán cho biến ở phía bên trái. Đó là cách ngôn ngữ hoạt động. Theo như "x ++" "return" x cho phương trình, đó là cách toán tử gia tăng hậu tố hoạt động (trả về giá trị của x, sau đó tăng nó). Nếu nó là "--x", thì nó sẽ là (tăng x, sau đó trả về giá trị). Trả lại không phải là từ đúng ở đó, nhưng bạn có ý tưởng.
RHSeeger

7

Bạn không thực sự cần mã máy để hiểu những gì đang xảy ra.

Theo định nghĩa:

  1. Toán tử gán đánh giá biểu thức phía bên phải và lưu trữ nó trong một biến tạm thời.

    1.1. Giá trị hiện tại của x được sao chép vào biến tạm thời này

    1.2. x được tăng lên ngay bây giờ.

  2. Biến tạm thời sau đó được sao chép vào phía bên trái của biểu thức, là x tình cờ! Vì vậy, đó là lý do tại sao giá trị cũ của x lại được sao chép vào chính nó.

Nó khá đơn giản.


5

Điều này là do nó không bao giờ được tăng lên trong trường hợp này. x++sẽ sử dụng giá trị của nó trước khi tăng như trong trường hợp này, nó sẽ giống như:

x = 0;

Nhưng nếu bạn làm ++x;điều này sẽ tăng lên.


nếu bạn thực hiện một bài kiểm tra, bạn có thể thấy rằng nó tăng đầu tiên và các thuộc tính sau. Vì vậy, nó không nên thuộc tính không.
Tom Brito

1
@Tom: xem câu trả lời của tôi - Tôi cho thấy trong một thử nghiệm rằng x ++ thực sự trả về giá trị cũ của x. Đó là nơi nó phá vỡ.
Robert Munteanu

"nếu bạn thực hiện một bài kiểm tra" - một số người dường như nghĩ rằng một bài kiểm tra viết bằng C cho chúng ta biết Java sẽ làm gì, khi đó nó thậm chí sẽ không cho chúng ta biết C sẽ làm gì.
Jim Balter

3

Giá trị giữ ở mức 0 vì giá trị x++là 0. Trong trường hợp này, không có vấn đề gì nếu giá trị của xtăng hay không, việc gán x=0được thực thi. Điều này sẽ ghi đè lên giá trị tăng tạm thời của x(là 1 trong "thời gian rất ngắn").


Nhưng x ++ là một hoạt động bài. Vì vậy, x sẽ phải được tăng lên sau khi hoàn thành nhiệm vụ.
Sagar V

1
@Sagar V: chỉ dành cho biểu thức x++, không phải cho toàn bộ bài tậpx=x++;
Progman

Không, tôi nghĩ rằng nó chỉ cần được tăng lên sau khi giá trị của x được sử dụng trong bài tập được đọc.
Rup

1

Điều này hoạt động như thế nào bạn mong đợi người khác. Đó là sự khác biệt giữa tiền tố và hậu tố.

int x = 0; 
while (x < 3)    x = (++x);

1

Hãy nghĩ về x ++ như một lời gọi hàm "trả về" giá trị X trước khi tăng (đó là lý do tại sao nó được gọi là tăng sau).

Vì vậy, thứ tự thao tác là:
1: cache giá trị của x trước khi tăng
2: tăng x
3: trả về giá trị được lưu (x trước khi tăng)
4: giá trị trả về được gán cho x


OK, nhưng những gì chỉ định thứ tự của bước 3 và 4?
Rup

"trả lại những gì X đã có trước khi tăng" là sai, hãy xem cập nhật của tôi
Tom Brito

Trong thực tế, bước 3 và 4 không phải là các hoạt động riêng biệt - nó không thực sự là một lệnh gọi hàm trả về một giá trị, nó chỉ giúp nghĩ về nó theo cách đó. Bất cứ khi nào bạn có một nhiệm vụ, phía bên phải được "đánh giá" thì kết quả được gán cho phía bên trái, kết quả đánh giá có thể được coi là giá trị trả về vì nó giúp bạn hiểu thứ tự các thao tác, nhưng nó không thực sự .
jhabbott

Rất tiếc, đúng. Ý tôi là bước 2 và 4 - tại sao giá trị trả về được lưu trữ trên đầu giá trị tăng dần?
Rup

1
Đây là một phần của định nghĩa của một thao tác gán, đầu tiên phía bên phải được đánh giá hoàn toàn, sau đó kết quả được gán cho phía bên trái.
jhabbott

1

Khi ++ ở trên rhs, kết quả được trả về trước khi số được tăng lên. Thay đổi thành ++ x và nó sẽ ổn thôi. Java sẽ tối ưu hóa điều này để thực hiện một thao tác duy nhất (gán x cho x) thay vì tăng.


1

Theo như tôi có thể thấy, lỗi xảy ra, do việc gán ghi đè giá trị tăng dần, với giá trị trước khi tăng, tức là nó hoàn tác gia tăng.

Cụ thể, biểu thức "x ++", có giá trị 'x' trước khi tăng so với "++ x" có giá trị 'x' sau khi tăng.

Nếu bạn quan tâm đến việc điều tra mã byte, chúng tôi sẽ xem xét ba dòng trong câu hỏi:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7: iload_1 # Sẽ đặt giá trị của biến cục bộ thứ 2 trên ngăn xếp
8: iinc 1,1 # sẽ tăng biến cục bộ thứ 2 lên 1, lưu ý rằng nó sẽ không bị ảnh hưởng!
9: istore_1 # Sẽ bật đầu ngăn xếp và lưu giá trị của phần tử này vào biến cục bộ thứ 2
(Bạn có thể đọc các hiệu ứng của từng lệnh JVM tại đây )

Đây là lý do tại sao đoạn mã trên sẽ lặp vô hạn, trong khi phiên bản có ++ x thì không. Mã byte cho ++ x sẽ trông khá khác biệt, theo như tôi nhớ từ trình biên dịch Java 1.3 tôi đã viết cách đây một năm, mã byte phải đi như thế này:

iinc 1,1
iload_1
istore_1

Vì vậy, chỉ cần hoán đổi hai dòng đầu tiên, thay đổi ngữ nghĩa sao cho giá trị còn lại trên đỉnh ngăn xếp, sau giá trị tăng (tức là 'giá trị' của biểu thức) là giá trị sau khi tăng.


1
    x++
=: (x = x + 1) - 1

Vì thế:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

Trong khi

   ++x
=: x = x + 1

Vì thế:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

Tất nhiên kết quả cuối cùng cũng giống như chỉ x++;hoặc ++x;trên một dòng.


0
 x = x++; (increment is overriden by = )

vì tuyên bố trên x không bao giờ đạt 3;


0

Tôi tự hỏi nếu có bất cứ điều gì trong đặc tả Java xác định chính xác hành vi của điều này. (Hàm ý rõ ràng của câu nói đó là tôi quá lười kiểm tra.)

Lưu ý từ mã byte của Tom, các dòng chính là 7, 8 và 11. Dòng 7 tải x vào ngăn xếp tính toán. Dòng 8 tăng x. Dòng 11 lưu giá trị từ ngăn xếp trở lại x. Trong trường hợp bình thường khi bạn không gán lại giá trị cho mình, tôi không nghĩ sẽ có bất kỳ lý do nào khiến bạn không thể tải, lưu trữ, sau đó tăng dần. Bạn sẽ nhận được kết quả tương tự.

Giống như, giả sử bạn có một trường hợp bình thường hơn khi bạn viết một cái gì đó như: z = (x ++) + (y ++);

Cho dù nó nói (mã giả để bỏ qua kỹ thuật)

load x
increment x
add y
increment y
store x+y to z

hoặc là

load x
add y
store x+y to z
increment x
increment y

nên không liên quan. Hoặc là thực hiện nên hợp lệ, tôi sẽ nghĩ.

Tôi cực kỳ thận trọng về việc viết mã phụ thuộc vào hành vi này. Nó trông rất phụ thuộc vào việc thực hiện, giữa các vết nứt đối với tôi. Lần duy nhất nó sẽ tạo ra sự khác biệt là nếu bạn làm điều gì đó điên rồ, như ví dụ ở đây hoặc nếu bạn có hai luồng đang chạy và phụ thuộc vào thứ tự đánh giá trong biểu thức.



0

Các x++biểu thức đánh giá đến x. Phần ++ảnh hưởng đến giá trị sau khi đánh giá , không phải sau tuyên bố . vì vậy x = x++được dịch thành hiệu quả

int y = x; // evaluation
x = x + 1; // increment part
x = y; // assignment

0

Trước khi tăng giá trị lên một, giá trị được gán cho biến.


0

Nó xảy ra bởi vì nó tăng bài. Nó có nghĩa là biến được tăng lên sau khi biểu thức được ước tính.

int x = 9;
int y = x++;

x bây giờ là 10, nhưng y là 9, giá trị của x trước khi nó được tăng lên.

Xem thêm trong Định nghĩa của bài tăng .


1
x/ yVí dụ của bạn khác với mã thực và sự khác biệt có liên quan. Liên kết của bạn thậm chí không đề cập đến Java. Đối với hai trong số những ngôn ngữ nó không đề cập đến, báo cáo kết quả trong câu hỏi là undefined.
Matthew Flaschen

0

Kiểm tra mã dưới đây,

    int x=0;
    int temp=x++;
    System.out.println("temp = "+temp);
    x = temp;
    System.out.println("x = "+x);

đầu ra sẽ là,

temp = 0
x = 0

post incrementcó nghĩa là tăng giá trị và trả về giá trị trước khi tăng . Đó là lý do tại sao giá trị temp0. Vì vậy, những gì nếu temp = ivà đây là trong một vòng lặp (ngoại trừ dòng mã đầu tiên). giống như trong câu hỏi !!!!


-1

Toán tử gia tăng được áp dụng cho cùng một biến như bạn đang gán. Đó là yêu cầu rắc rối. Tôi chắc chắn rằng bạn có thể thấy giá trị của biến x của mình trong khi chạy chương trình này .... điều đó sẽ làm rõ lý do tại sao vòng lặp không bao giờ kết thúc.

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.