30/09/2018, 17:33

Thảo luận về ví dụ với toán tử ++ trong C

Em có thử chạy chương trình sau trên hai trình biên dịch khác nhau là VS2013 trên Win 7 và gcc trên Ubuntu và cho ra hai kết quả khác nhau. Các bác cùng thử thảo luận xem vì sao lại có kết quả như thế nhé:

#include "stdio.h"
void main()
{
    int i = 5, j = 5, y;
    int x = (++i) + (++i) + (++i);
    y = (++j) + (++j) + (++j);
    printf("x = %d, y = %d, i = %d, j = %d
", x, y , i, j);
}

Kết quả:

Trên VS2013: x = 24, y =24, i = 8, j = 8
Trên Ubuntu: x = 22, y =22, i = 8, j = 8

*grab popcorn* viết 19:39 ngày 30/09/2018

Cái này gọi là Undefined Behavior.
Tức tùy vào trình biên dịch thì nó sẽ cho ra kết quả khác nhau.

NBQ viết 19:47 ngày 30/09/2018

Bác có thể nói rõ hơn được không? Cách tính của hai trình biên dịch kia như thế nào mà lại ra được kết quả như thế bác?

Chi Ngo viết 19:44 ngày 30/09/2018

Theo mình thì rất có thể do cách tính của biếu thức dưới dạng ký pháp Balan. Như vậy, với compiler VS2013 thì biểu thức hậu tố được viết dưới dạng (++i)(++i)(++i)++ còn với compiler GCC (ubuntu) thì sẽ có dạng (++i)(++i)+(++i)+.
Vì theo thứ tự tính toán phép ++ sẽ thực hiện trước phép + nên với VS2013 thì phép ++i sẽ được tính 3 lần liên tục vì thế i = 8 và tổng sẽ là 24. Còn với GCC thì hai phép ++ đầu được thực hiện sau đó thực hiện phép + thứ nhất được tổng là 14 và cộng với phép cộng thứ hai với ++i lúc này bằng 8 nên được kết quả là 14+8 = 22.
Theo mình là như trên, để mình tìm hiểu kĩ lại và có câu trả lời thích đáng sau nhé!

Gió viết 19:35 ngày 30/09/2018

Muốn giải thích nó tại sao lại ra kq như vậy thì phải xem mã assembler nó sinh ra thôi. Nói chung undefined behavior này không tiên đoán dc kết quả. Tốt nhất là tránh nó

NBQ viết 19:44 ngày 30/09/2018

Bác biết cách xuất ra mã assembly không thử xuất ra xem cách xử lý của compiler cho em và những bạn muốn biết hiểu với được không ạ

Muốn giải thích nó tại sao lại ra kq như vậy thì phải xem mã assembler nó sinh ra thôi. Nói chung undefined behavior này không tiên đoán dc kết quả. Tốt nhất là tránh nó

NBQ viết 19:45 ngày 30/09/2018

Theo mình thì rất có thể do cách tính của biếu thức dưới dạng ký pháp Balan. Như vậy, với compiler VS2013 thì biểu thức hậu tố được viết dưới dạng (++i)(++i)(++i)++ còn với compiler GCC (ubuntu) thì sẽ có dạng (++i)(++i)+(++i)+.
Vì theo thứ tự tính toán phép ++ sẽ thực hiện trước phép + nên với VS2013 thì phép ++i sẽ được tính 3 lần liên tục vì thế i = 8 và tổng sẽ là 24. Còn với GCC thì hai phép ++ đầu được thực hiện sau đó thực hiện phép + thứ nhất được tổng là 14 và cộng với phép cộng thứ hai với ++i lúc này bằng 8 nên được kết quả là 14+8 = 22.
Theo mình là như trên, để mình tìm hiểu kĩ lại và có câu trả lời thích đáng sau nhé!

E là compiler tính theo cách khác bác ạ

Gió viết 19:40 ngày 30/09/2018

Nếu mà linux thì dùng lệnh gcc -S [file name] -o [file out].s sau đó có thể xem mã asm. Nói chung cái này mình cũng không rành lắm, thử hỏi anh @ltd xem

Chi Ngo viết 19:36 ngày 30/09/2018

E là compiler tính theo cách khác bác ạ

Mình lưu ý chút là compiler không tính toán gì cả nhé, chỉ chuyển từ ngôn ngữ bậc cao xuông ngôn ngữ bậc thấp hơn và việc chuyển một biểu thức phải sử dụng một thuật toán hay phương pháp nào đó (ở đây mình chỉ biết biểu thức hậu tố thôi).

*grab popcorn* viết 19:35 ngày 30/09/2018

Window thì cứ down ollydbg về load file là đọc được ngay.
Còn linux thì mình chịu.
Bây giờ chỉ còn vấn đề là bạn hiểu hay ko Do asm ko như C đâu nhé

Minh Hoàng viết 19:36 ngày 30/09/2018
   0x0040136b <+55>:    mov    %eax,0x24(%esp)
   0x0040136f <+59>:    incl   0x28(%esp) //i++
   0x00401373 <+63>:    incl   0x28(%esp) //i++
   0x00401377 <+67>:    mov    0x28(%esp),%eax //chuyen vao eax
   0x0040137b <+71>:    lea    (%eax,%eax,1),%edx // <=> edx=eax+eax
   0x0040137e <+74>:    incl   0x28(%esp) // i++
   0x00401382 <+78>:    mov    0x28(%esp),%eax // chuyen vao eax
   0x00401386 <+82>:    add    %edx,%eax // cong edx va eax

của mình chạy trên windows, dùng gcc để biên dịch.rồi disassemble bằng gdb.
chạy chương trình được x = 22, y = 22, i = 8, j = 8
đoạn phía trên là của biến y. do i cộng thêm 1 2 lần (giờ i=7), sau đó edx=eax+eax=14, tiếp theo tăng lên 1 được (i=8). ta có 7+7+8=22
còn bên VS thì mình nghĩ nó sẽ cộng hết một lượt nên mới được 24.

Bài liên quan
0