Sự Khác Biệt Giữa ++ i Và i ++ Là Gì?

Nhiều bạn mới lập trình cũng như lập trình được một khoảng thời gian vẫn chưa phân biệt được sự khác nhau giữa i++ và ++i là gì? Ở bài này mình sẽ lấy ra một ví dụ đơn giản nhất để các bạn có thể nắm được sự khác nhau này.

Hồi học ở CLB lập trình của trường, ông thầy code về vòng lặp, cái for nào mình cũng dùng++i làm mình vô cùng thắc mắc. Cũng thấy làm lạ tại sao ko dùng i++. Về nhà hỏi giáo sư Google với mấy người ở cảnh giới cao hơn mới được thông não về nó.

1. Điểm qua các ví dụ để nắm rõ hơn về sự khác biệt:

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

 

++i tăng giá trị của i lên 1 và trả về giá trị mới đó.

i++ cũng tương tự nhưng giá trị trả về là giá trị ban đầu của i trước khi được tăng lên 1.

Một điểm đáng lưu ý ở đây, không nên nhầm lẫn là i++ sẽ trả về giá trị i cho phép gán trước khi nó được tăng lên. Phép gán luôn thực hiện sau cùng, nên điều đó là không thể nhé.

Về bản chất chương trình sẽ tạo ra một biến tạm (temp) để lưu giá trị ban đầu của i và trả về giá trị đó cho phép gán sau khi phép toán i++ thực hiện xong.

Một vi dụ khác:

#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

2. Cơ chế hoạt động của ++i và i++ sẽ như thế nào qua 2 ví dụ trên?

  • ++i tính luôn trên giá trị ban đầu.
  • i++ tạo ra bản copy của giá trị ban đầu.

Cho nên đối với những kiểu dữ liệu phức tạp như lớp (class), …v.v. có thể i++ sẽ lâu hơn một chút so với ++i bởi vì nó phải tạo ra bản copy của đối tượng.

Còn sử dụng ++i và i++ với vai trò là biến đếm trong vòng lặp for là như nhau cả về tốc độ thực thi và tăng dần i, bạn có thể tùy thích sử dụng trường hợp nào cũng được.

3. 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++)*2và (++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:

4. Performance

Tuy phải tốn công tạo một biến temp cho i++ nhưng thực tế sự khác biệt về performance là không lớn. Hầu như tất cả compiler hiện đại sẽ optimize phép toán đó. Bằng chứng là trong ví dụ này trên stackoverflow, gcc cho kết quả biên dịch là như nhau cho hai file code chứa vòng lặp xài ++i và i++.

Nhưng riêng với C++, sự chênh lệch có thể là đáng kể trong một số trường hợp. Cụ thể là với user-defined type, tức là class bạn tạo ra, vì operator++() là một hàm và compiler không biết làm cách nào để optimize việc tạo ra cái temp object trong đó cả. Sao nó biết được bạn define cái gì và sẽ bự cỡ nào trong class.

5. Vậy nên xài cái nào trong vòng for?

Riêng đối với mình thì ++i đã trở thành một convention bất thành văn rồi. Dù chiến project nào mình cũng xài nó mà chả quan tâm performance gì cho mệt óc. Rất nhiều guru trong giới coder đều đưa ra lời khuyên:

“In any case, follow the guideline “prefer ++i over i++” and you won’t go wrong”

6. Kết bài

Hy vọng bài này sẽ giúp ích cho các bạn khi mới lập trình!

Comment ở dưới bài viết nếu gặp khó khăn gì trong lập trình nhé, mình sẽ hỗ trợ.