2011-10-24 39 views
5
int main(void) 
{ 
    int i = 0; 
    i = ++i % 3; 
    return 0; 
} 

tôi biên dịch nó như thế này:Tại sao điều này (i = ++ i% 3) tạo ra cảnh báo: "có thể không xác định"?

$ gcc -Wall main.c -o main 
main.c: In function ‘main’: 
main.c:4: warning: operation on ‘i’ may be undefined 

Tại sao biên dịch nói i chưa undefined?

+2

Thao tác * * không được xác định, không phải là 'i'. I E. đó là hành vi không xác định. Đừng làm thế. –

+0

@Paul R tại sao bạn cho rằng hoạt động không xác định? ++ i tăng i đến 1 và modulo opeartor 1 với 3 cho 1 là kết quả. không làm việc? – niko

+1

@niko: Xem câu trả lời của tôi và http://www.catb.org/jargon/html/N/nasal-demons.html –

Trả lời

4

Như những người khác đã chỉ ra, hành vi này là undefined:

6,5 Expressions
...
2 Giữa điểm chuỗi trước và bên cạnh một đối tượng có trách nhiệm lưu trữ giá trị của nó sửa đổi nhiều nhất một lần bằng cách đánh giá một biểu thức. 72) Hơn nữa, giá trị trước chỉ được đọc để xác định giá trị được lưu trữ. 73)
...
72) Cờ trạng thái dấu phẩy không phải là đối tượng và có thể được đặt nhiều lần trong một biểu thức. 73) Đoạn này làm cho biểu thức tuyên bố không xác định như
 
    i = ++i + 1; 
    a[i++] = i; 
trong khi cho phép
 
    i = i + 1; 
    a[i] = i; 

Khái niệm i = ++i % 3 nỗ lực để thay đổi giá trị chứa trong ihai lần trước khi các điểm trình tự tiếp theo (trong trường hợp này, ; kết thúc tuyên bố), một lần bằng cách đánh giá ++i và một lần bằng cách đánh giá biểu thức gán lớn hơn.

Bây giờ, tại sao đây lại là một vấn đề? Sau khi tất cả, C# và Java có thể xử lý các biểu thức này tốt.

Vấn đề là, với vài ngoại lệ, C không đảm bảo rằng toán hạng trong biểu thức được đánh giá theo bất kỳ thứ tự cụ thể nào hoặc tác dụng phụ của biểu thức sẽ được áp dụng ngay sau khi biểu thức được đánh giá (không giống C# và Java, làm cho những đảm bảo đó).Ví dụ: biểu thức ++i có kết quả (i + 1) và hiệu ứng phụ (tăng giá trị được lưu trữ trong i); tuy nhiên, hiệu ứng phụ có thể được trì hoãn cho đến khi biểu thức lớn hơn được đánh giá. IOW, chuỗi hành động sau được cho phép:

 
    t0 = i + 1 
    t1 = t0 % 3 
    i = t1 
    i = i + 1 

Oopsie. Không phải những gì chúng tôi muốn.

Đây là một quyết định thiết kế có chủ ý; ý tưởng là nó cho phép các trình biên dịch sắp xếp lại các đánh giá một cách tối ưu (bằng cách tận dụng một giá trị đã có trong sổ đăng ký, nói). Nhược điểm là sự kết hợp nhất định của các biểu thức sẽ có kết quả không thể đoán trước.

6

Trong tiêu chuẩn, hành vi không xác định vì i được sửa đổi hai lần mà không có điểm chuỗi xen kẽ.

i = ++i % 3; 

Nhưng đó không thực sự là vấn đề. Điểm thực là: tại sao mọi người lại viết mã như vậy?

Giá trị bạn muốn i là bao nhiêu? Nếu bạn chỉ định một giá trị hoàn toàn mới vào i với i = ..., bạn đang cố gắng đạt được hiệu quả nào với ++i? Nếu đây là song song-vũ trụ-C trong đó mã như thế này thực sự có ý nghĩa, thì - trong trường hợp tốt nhất - số tăng i được thay thế ngay lập tức bằng toàn bộ giá trị mới được gán. Vậy tại sao không chỉ viết nó là

i = (i+1) % 3; 

cũng đúng trong C-as-we-know-it.

+1

+1 cho "tại sao mọi người lại viết mã như vậy?" –

+0

+1 cho "tại sao mọi người lại viết mã như vậy?" :) –

Các vấn đề liên quan