C: sự khác biệt giữa ++ i và i ++ là gì?


888

Trong C, sự khác biệt giữa việc sử dụng ++ii++nên được sử dụng trong khối tăng của một forvòng lặp là gì?


10
Không chắc chắn người đăng ban đầu quan tâm, nhưng trong C ++, sự khác biệt về hiệu suất có thể là đáng kể, vì việc tạo đối tượng tạm thời có thể tốn kém cho loại do người dùng xác định.
Trên Freund

Câu trả lời:


1101
  • ++isẽ tăng giá trị của i, và sau đó trả về giá trị tăng.

     i = 1;
     j = ++i;
     (i is 2, j is 2)
  • i++sẽ tăng giá trị của i, nhưng trả về giá trị ban đầu iđược giữ trước khi tăng.

     i = 1;
     j = i++;
     (i is 2, j is 1)

Đối với một forvòng lặp, hoặc hoạt động. ++idường như phổ biến hơn, có lẽ vì đó là những gì được sử dụng trong K & R .

Trong mọi trường hợp, hãy làm theo hướng dẫn "thích ++ihơn i++" và bạn sẽ không đi sai.

Có một vài ý kiến ​​liên quan đến hiệu quả của ++ii++. Trong bất kỳ trình biên dịch phi sinh viên dự án, sẽ không có sự khác biệt hiệu suất. Bạn có thể xác minh điều này bằng cách xem mã được tạo, mã này sẽ giống hệt nhau.

Câu hỏi về hiệu quả rất thú vị ... đây là nỗ lực của tôi trong câu trả lời: Có sự khác biệt về hiệu năng giữa i ++ và ++ i trong C không?

Như @OnFreund lưu ý, nó khác với một đối tượng C ++, vì đây operator++()là một hàm và trình biên dịch không thể biết để tối ưu hóa việc tạo ra một đối tượng tạm thời để giữ giá trị trung gian.


6
Hiệu ứng này có làm thời tiết vòng lặp chạy một lần nữa khi đạt đến điều kiện cuối không? Ví dụ, for(int i=0; i<10; i++){ print i; } điều này sẽ không khác với for(int i=0; i<10; ++i){ print i; } sự hiểu biết của tôi là một số ngôn ngữ sẽ cung cấp cho bạn các kết quả khác nhau tùy thuộc vào việc bạn sử dụng.
aVeRTRAC

27
jonnyflash, cả hai sẽ hoạt động giống hệt nhau, vì sự gia tăng của i và bản in nằm trong các tuyên bố khác nhau. Đây phải là trường hợp cho bất kỳ ngôn ngữ nào hỗ trợ C-style ++. Sự khác biệt duy nhất giữa ++ i và i ++ sẽ là khi sử dụng giá trị của thao tác trong cùng một câu lệnh.
Đánh dấu Harrison

16
Vì trong hầu hết các trường hợp họ tạo ra mã giống hệt nhau, tôi thích i++vì nó có dạng "toán tử toán tử", là một phép gán "toán hạng-toán tử-giá trị". Nói cách khác, toán hạng đích nằm ở bên trái của biểu thức, giống như nó nằm trong một câu lệnh gán.
David R Tribble

2
@MarkHarrison, nó sẽ hoạt động giống hệt nhau không phải vì i++print inằm trong các tuyên bố khác nhau, mà bởi vì i++;i<10là. Nhận xét của @ jonnyflash không phải là không có cơ sở. Giả sử bạn có for(int i=0; i++<10){ print i; }for(int i=0; ++i<10){ print i; }. Chúng sẽ hoạt động khác nhau theo cách mà @johnnyflash mô tả trong bình luận đầu tiên.
Adam

3
@sam, vì trong một vòng lặp điển hình cho không có tác dụng phụ (ví dụ: gán) trong phần ++ i.
Mark Harrison

175

i ++ được gọi là Post Increment trong khi ++ i được gọi là Pre Increment.

i++

i++là gia tăng bài đăng bởi vì nó tăng igiá trị của 1 sau khi hoạt động kết thúc.

Hãy xem ví dụ sau:

int i = 1, j;
j = i++;

Ở đây giá trị của j = 1nhưng i = 2. Ở đây giá trị của isẽ được gán cho jđầu tiên sau đó isẽ được tăng lên.

++i

++ilà gia tăng trước bởi vì nó tăng igiá trị của 1 trước khi hoạt động. Nó có nghĩa là j = i;sẽ thực hiện sau i++.

Hãy xem ví dụ sau:

int i = 1, j;
j = ++i;

Ở đây giá trị của j = 2nhưng i = 2. Ở đây giá trị của isẽ được gán cho jsau khi i tăng i. Tương tự ++isẽ được thực hiện trước j=i;.

Đối với câu hỏi của bạn nên được sử dụng trong khối tăng của vòng lặp for? Câu trả lời là, bạn có thể sử dụng bất kỳ một .. không quan trọng. Nó sẽ thực hiện vòng lặp for của bạn giống như không. thời gian.

for(i=0; i<5; i++)
   printf("%d ",i);

for(i=0; i<5; ++i)
   printf("%d ",i);

Cả hai vòng lặp sẽ tạo ra cùng một đầu ra. tức 0 1 2 3 4.

Nó chỉ quan trọng khi bạn đang sử dụng nó.

for(i = 0; i<5;)
    printf("%d ",++i);

Trong trường hợp này đầu ra sẽ được 1 2 3 4 5.


1
Khởi tạo các biến sau tiền tố và sửa lỗi giúp hiểu. Cảm ơn.
Abdul Alim Shakir

42

Xin đừng lo lắng về "hiệu quả" (tốc độ, thực sự) trong đó cái nào nhanh hơn. Chúng tôi có trình biên dịch những ngày này chăm sóc những điều này. Sử dụng bất cứ ai có ý nghĩa để sử dụng, dựa trên đó thể hiện rõ hơn ý định của bạn.


1
trong đó, tôi hy vọng, có nghĩa là ' sử dụng tiền tố (inc | dec) rement trừ khi bạn thực sự cần giá trị cũ trước (inc | dec), điều mà rất ít người làm, và tỷ lệ đáng kinh ngạc của các tài liệu giảng dạy được sử dụng, tạo ra sự sùng bái hàng hóa của người dùng postfix, những người thậm chí không biết nó là gì '..!
underscore_d

Tôi không chắc chắn rằng "trình biên dịch ngày nay ... chăm sóc những thứ này" là hoàn toàn đúng. Trong một tùy chỉnh operator++(int)(phiên bản postfix), mã khá nhiều phải tạo tạm thời sẽ được trả về. Bạn có chắc chắn rằng trình biên dịch luôn có thể tối ưu hóa điều đó không?
Peter - Tái lập Monica

36

++i tăng giá trị, sau đó trả về nó.

i++ trả về giá trị, và sau đó tăng nó.

Đó là một sự khác biệt tinh tế.

Đối với một vòng lặp for, sử dụng ++i, vì nó nhanh hơn một chút. i++sẽ tạo ra một bản sao thêm mà chỉ cần được ném đi.


23
Tôi không biết về bất kỳ trình biên dịch nào, nơi nó tạo ra sự khác biệt cho các số nguyên ít nhất.
blabla999

4
không nhanh hơn . Các giá trị bị bỏ qua (chỉ có hiệu ứng phụ là có hiệu lực) và trình biên dịch có thể / sẽ tạo chính xác cùng một mã.
wildplasser

31

i++: Trong kịch bản này trước tiên, giá trị được gán và sau đó gia tăng xảy ra.

++i: Trong kịch bản này, đầu tiên gia tăng được thực hiện và sau đó giá trị được gán

Dưới đây là hình ảnh trực quan và đây cũng là một video thực tế hay thể hiện điều tương tự.

nhập mô tả hình ảnh ở đây


Làm thế nào bạn có thể tăng phần nào không được chỉ định?
Kouty

@kouty Bạn có thể tăng một thanh ghi không được gán cho một biến.
Polluks

20

Nguyên nhân ++i thể nhanh hơn một chút i++là vì i++có thể yêu cầu một bản sao cục bộ của giá trị của i trước khi nó được tăng lên, trong khi ++ikhông bao giờ. Trong một số trường hợp, một số trình biên dịch sẽ tối ưu hóa nó đi nếu có thể ... nhưng không phải lúc nào cũng có thể, và không phải tất cả các trình biên dịch đều làm điều này.

Tôi cố gắng không phụ thuộc quá nhiều vào tối ưu hóa trình biên dịch, vì vậy tôi làm theo lời khuyên của Ryan Fox: khi tôi có thể sử dụng cả hai, tôi sử dụng ++i.


11
-1 cho câu trả lời C ++ cho câu hỏi C. Không có nhiều "bản sao cục bộ" của giá trị ihơn giá trị 1 khi bạn viết một câu lệnh 1;.
R .. GitHub DỪNG GIÚP ICE

14

Kết quả hiệu quả của việc sử dụng hoặc trong một vòng lặp là giống hệt nhau. Nói cách khác, vòng lặp sẽ làm điều tương tự chính xác trong cả hai trường hợp.

Về hiệu quả, có thể có một hình phạt liên quan đến việc chọn i ++ hơn ++ i. Về mặt đặc tả ngôn ngữ, sử dụng toán tử tăng sau sẽ tạo thêm một bản sao của giá trị mà toán tử đang thực hiện. Đây có thể là một nguồn của các hoạt động thêm.

Tuy nhiên, bạn nên xem xét hai vấn đề chính với logic trước đó.

  1. Trình biên dịch hiện đại là tuyệt vời. Tất cả các trình biên dịch tốt đều đủ thông minh để nhận ra rằng nó đang nhìn thấy một số nguyên tăng trong một vòng lặp for và nó sẽ tối ưu hóa cả hai phương thức thành cùng một mã hiệu quả. Nếu việc sử dụng tăng sau so với tăng trước thực sự khiến chương trình của bạn có thời gian chạy chậm hơn, thì bạn đang sử dụng một trình biên dịch khủng .

  2. Về độ phức tạp thời gian hoạt động, hai phương pháp (ngay cả khi một bản sao đang thực sự được thực hiện) là tương đương. Số lượng các lệnh được thực hiện bên trong vòng lặp sẽ chi phối đáng kể số lượng các hoạt động trong hoạt động gia tăng đáng kể. Do đó, trong bất kỳ vòng lặp nào có kích thước đáng kể, hình phạt của phương pháp tăng sẽ bị lu mờ ồ ạt khi thực hiện thân vòng lặp. Nói cách khác, bạn tốt hơn nhiều là lo lắng về việc tối ưu hóa mã trong vòng lặp hơn là gia tăng.

Theo tôi, toàn bộ vấn đề chỉ đơn giản là nắm bắt một sở thích phong cách. Nếu bạn nghĩ rằng tăng trước dễ đọc hơn, thì hãy sử dụng nó. Cá nhân, tôi thích phần tăng sau, nhưng đó có lẽ là vì đó là điều tôi được dạy trước khi tôi biết bất cứ điều gì về tối ưu hóa.

Đây là một ví dụ tinh túy về tối ưu hóa sớm và các vấn đề như thế này có khả năng đánh lạc hướng chúng ta khỏi các vấn đề nghiêm trọng trong thiết kế. Tuy nhiên, đây vẫn là một câu hỏi hay để hỏi vì không có sự thống nhất trong cách sử dụng hoặc sự đồng thuận trong "thực tiễn tốt nhất".


13

Cả hai đều tăng số lượng. ++itương đương với i = i + 1.

i++++irất giống nhau nhưng không hoàn toàn giống nhau. Cả hai đều tăng số, nhưng ++ităng số trước khi biểu thức hiện tại được ước tính, trong khii++ tăng số sau khi biểu thức được ước tính.

Thí dụ:

int i = 1;
int x = i++; //x is 1, i is 2
int y = ++i; //y is 3, i is 3

8

++i(Hoạt động tiền tố): Tăng và sau đó gán giá trị
(ví dụ) : int i = 5, int b = ++i Trong trường hợp này, 6 được gán cho b trước và sau đó tăng lên 7 và cứ thế.

i++(Hoạt động Postfix): Gán và sau đó tăng giá trị
(ví dụ) : int i = 5,int b = i++ Trong trường hợp này, 5 được gán cho b trước và sau đó tăng lên 6 và cứ thế.

Incase of for loop: i++chủ yếu được sử dụng bởi vì, thông thường chúng ta sử dụng giá trị bắt đầu itrước khi tăng trong vòng lặp for. Nhưng tùy thuộc vào logic chương trình của bạn, nó có thể khác nhau.


7

++i: là tăng trước, khác là tăng sau.

i++: lấy phần tử và sau đó tăng nó.
++i: tăng i và sau đó trả về phần tử.

Thí dụ:

int i = 0;
printf("i: %d\n", i);
printf("i++: %d\n", i++);
printf("++i: %d\n", ++i);

Đầu ra:

i: 0
i++: 0
++i: 2

5

Tôi giả sử bạn hiểu sự khác biệt về ngữ nghĩa bây giờ (mặc dù thật lòng tôi tự hỏi tại sao mọi người hỏi 'câu hỏi của nhà điều hành X có nghĩa là gì' trên stack stack thay vì đọc, bạn biết, một cuốn sách hoặc hướng dẫn web hoặc một cái gì đó.

Nhưng dù sao, theo như sử dụng cái nào, hãy bỏ qua các câu hỏi về hiệu năng, điều không thể quan trọng ngay cả trong C ++. Đây là nguyên tắc bạn nên sử dụng khi quyết định sử dụng:

Nói những gì bạn có nghĩa trong mã.

Nếu bạn không cần giá trị gia tăng trước trong tuyên bố của mình, đừng sử dụng hình thức của nhà điều hành. Đó là một vấn đề nhỏ, nhưng trừ khi bạn đang làm việc với một hướng dẫn phong cách cấm hoàn toàn một phiên bản có lợi cho phiên bản kia (hay còn gọi là hướng dẫn kiểu đầu xương), bạn nên sử dụng biểu mẫu thể hiện chính xác nhất những gì bạn đang cố gắng thực hiện.

QED, sử dụng phiên bản tăng trước:

for (int i = 0; i != X; ++i) ...

5

Sự khác biệt có thể được hiểu bởi mã C ++ đơn giản dưới đây:

int i, j, k, l;
i = 1; //initialize int i with 1
j = i+1; //add 1 with i and set that as the value of j. i is still 1
k = i++; //k gets the current value of i, after that i is incremented. So here i is 2, but k is 1
l = ++i; // i is incremented first and then returned. So the value of i is 3 and so does l.
cout << i << ' ' << j << ' ' << k << ' '<< l << endl;
return 0;

5

Sự khác biệt chính là

  • i ++ Bài đăng ( Sau khi tăng ) và
  • ++ i Pre ( Trước khi tăng )

    • đăng nếu i =1 vòng lặp tăng như1,2,3,4,n
    • trước nếu i =1 vòng lặp tăng như2,3,4,5,n

5

i ++ và ++ tôi

Mã nhỏ này có thể giúp hình dung sự khác biệt từ một góc độ khác với các câu trả lời đã được đăng:

int i = 10, j = 10;

printf ("i is %i \n", i);
printf ("i++ is %i \n", i++);
printf ("i is %i \n\n", i);

printf ("j is %i \n", j);
printf ("++j is %i \n", ++j);
printf ("j is %i \n", j);

Kết quả là:

//Remember that the values are i = 10, and j = 10

i is 10 
i++ is 10     //Assigns (print out), then increments
i is 11 

j is 10 
++j is 11    //Increments, then assigns (print out)
j is 11 

Hãy chú ý đến các tình huống trước và sau.

cho vòng lặp

Về việc một trong số chúng nên được sử dụng trong khối tăng của vòng lặp for, tôi nghĩ rằng cách tốt nhất chúng ta có thể làm để đưa ra quyết định là sử dụng một ví dụ hay:

int i, j;

for (i = 0; i <= 3; i++)
    printf (" > iteration #%i", i);

printf ("\n");

for (j = 0; j <= 3; ++j)
    printf (" > iteration #%i", j);

Kết quả là:

> iteration #0 > iteration #1 > iteration #2 > iteration #3
> iteration #0 > iteration #1 > iteration #2 > iteration #3 

Tôi không biết về bạn, nhưng tôi không thấy bất kỳ sự khác biệt nào trong cách sử dụng, ít nhất là trong một vòng lặp for.


5

Đoạn mã C sau đây minh họa sự khác biệt giữa các toán tử tăng và giảm trước và sau:

int  i;
int  j;

Toán tử gia tăng:

i = 1;
j = ++i;    // i is now 2, j is also 2
j = i++;    // i is now 3, j is 2

4

Pre-crement có nghĩa là tăng trên cùng một dòng. Tăng sau có nghĩa là tăng sau khi dòng thực thi.

int j=0;
System.out.println(j); //0
System.out.println(j++); //0. post-increment. It means after this line executes j increments.

int k=0;
System.out.println(k); //0
System.out.println(++k); //1. pre increment. It means it increments first and then the line executes

Khi đi kèm với các toán tử OR, AND, nó trở nên thú vị hơn.

int m=0;
if((m == 0 || m++ == 0) && (m++ == 1)) { //false
/* in OR condition if first line is already true then compiler doesn't check the rest. It is technique of compiler optimization */
System.out.println("post-increment "+m);
}

int n=0;
if((n == 0 || n++ == 0) && (++n == 1)) { //true
System.out.println("pre-increment "+n); //1
}

Trong mảng

System.out.println("In Array");
int[] a = { 55, 11, 15, 20, 25 } ;
int ii, jj, kk = 1, mm;
ii = ++a[1]; // ii = 12. a[1] = a[1] + 1
System.out.println(a[1]); //12

jj = a[1]++; //12
System.out.println(a[1]); //a[1] = 13

mm = a[1];//13
System.out.printf ( "\n%d %d %d\n", ii, jj, mm ) ; //12, 12, 13

for (int val: a) {
     System.out.print(" " +val); //55, 13, 15, 20, 25
}

Trong C ++ bài / tăng trước của biến con trỏ

#include <iostream>
using namespace std;

int main() {

    int x=10;
    int* p = &x;

    std::cout<<"address = "<<p<<"\n"; //prints address of x
    std::cout<<"address = "<<p<<"\n"; //prints (address of x) + sizeof(int)
    std::cout<<"address = "<<&x<<"\n"; //prints address of x

    std::cout<<"address = "<<++&x<<"\n"; //error. reference can't re-assign because it is fixed (immutable)
}

4

Ngắn gọn:

++ii++hoạt động tương tự nếu bạn không viết chúng trong một hàm. Nếu bạn sử dụng một cái gì đó như function(i++)hoặc function(++i)bạn có thể thấy sự khác biệt.

function(++i)cho biết lần đầu tiên tăng i lên 1, sau đó đưa igiá trị này vào hàm với giá trị mới.

function(i++)nói đặt đầu tiên ivào hàm sau khi tăng i1.

int i=4;
printf("%d\n",pow(++i,2));//it prints 25 and i is 5 now
i=4;
printf("%d",pow(i++,2));//it prints 16 i is 5 now

2
Sự khác biệt không thực sự gắn liền với các cuộc gọi chức năng (và bạn có thể phát hiện sự khác biệt mà không thực hiện các cuộc gọi chức năng). Có một sự khác biệt giữa int j = ++i;int k = i++;ngay cả khi không có chức năng gọi liên quan.
Jonathan Leffler

3

Sự khác biệt duy nhất là thứ tự các hoạt động giữa mức tăng của biến và giá trị mà toán tử trả về.

Mã này và đầu ra của nó giải thích sự khác biệt:

#include<stdio.h>

int main(int argc, char* argv[])
{
  unsigned int i=0, a;
  a = i++;
  printf("i before: %d; value returned by i++: %d, i after: %d\n", i, a, i);
  i=0;
  a = ++i;
  printf("i before: %d; value returned by ++i: %d, i after: %d\n", i, a, i);
}

Đầu ra là:

i before: 1; value returned by i++: 0, i after: 1
i before: 1; value returned by ++i: 1, i after: 1

Vì vậy, về cơ bản ++itrả về giá trị sau khi nó được tăng lên, trong khi ++itrả về giá trị trước khi nó được tăng lên. Cuối cùng, trong cả hai trường hợp, igiá trị của nó sẽ tăng lên.

Một vi dụ khac:

#include<stdio.h>

int main ()
  int i=0;
  int a = i++*2;
  printf("i=0, i++*2=%d\n", a);
  i=0;
  a = ++i * 2;
  printf("i=0, ++i*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  return 0;
}

Đầu ra:

i=0, i++*2=0
i=0, ++i*2=2
i=0, (++i)*2=2
i=0, (++i)*2=2

Nhiều khi không có sự khác biệt

Sự khác biệt là rõ ràng khi giá trị trả về được gán cho một biến khác hoặc khi gia tăng được thực hiện kết hợp với các hoạt động khác trong đó các hoạt động được ưu tiên áp dụng ( i++*2khác với ++i*2, (i++)*2(++i)*2trả về cùng một giá trị) trong nhiều trường hợp chúng có thể hoán đổi cho nhau. Một ví dụ cổ điển là cú pháp vòng lặp for:

for(int i=0; i<10; i++)

có tác dụng tương tự

for(int i=0; i<10; ++i)

Quy tắc cần nhớ

Để không gây nhầm lẫn giữa hai nhà khai thác, tôi đã áp dụng quy tắc này:

Liên kết vị trí của toán tử ++đối với biến ivới thứ tự của phép ++toán đối với phép gán

Nói cách khác:

  • ++ trước khi i tăng có nghĩa là phải được thực hiện trước khi chuyển nhượng;
  • ++ sau khi i tăng có nghĩa là phải được thực hiện sau khi gán:

3

Bạn có thể nghĩ về chuyển đổi nội bộ của nó như là một báo cáo nhiều ;

  • trường hợp 1
i++;

bạn có thể nghĩ nó như là,

i;
i = i+1;
  • trường hợp 2
++i;

bạn có thể nghĩ nó như là,

i = i+i;
i;

-3

a = i ++ có nghĩa là a chứa giá trị i hiện tại a = ++ i có nghĩa là giá trị chứa i tăng


10
Câu trả lời này không chính xác. a = i++;có nghĩa là giá trị được lưu trữ asẽ là giá trị itrước khi tăng, nhưng 'không tăng' ngụ ý ikhông tăng, điều này hoàn toàn sai - iđược tăng lên, nhưng giá trị của biểu thức là giá trị trước khi tăng.
Jonathan Leffler

-6

Dưới đây là ví dụ để hiểu sự khác biệt

int i=10;
printf("%d %d",i++,++i);

đầu ra: 10 12/11 11(tùy theo thứ tự đánh giá các đối số choprintf hàm, khác nhau giữa các trình biên dịch và kiến ​​trúc)

Giải thích: i++-> iđược in, và sau đó tăng. (In 10, nhưng isẽ trở thành 11) ++i-> ităng giá trị và in giá trị. (In 12 và giá trị của i12)


11
Điều này gây ra hành vi không xác định vì không có điểm thứ tự giữa i++++i
MM

@Lundin là đúng, mặc dù LHS, RHS của dấu phẩy có điểm thứ tự giữa chúng nhưng 2 biểu thức vẫn không được giải quyết cho nhau
Antti Haapala
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.