2012-03-04 39 views
16

Có rất nhiều câu hỏi thú vị nâng here về hành vi không xác định trong C. Một trong số đó là (chút thay đổi)Có 'a [i] = i;' luôn luôn dẫn đến hành vi được xác định rõ ràng?

Liệu các mảnh sau kết quả mã trong hành vi undefined?

int i = 0, *a = &i; // Line 1 
a[i] = i + 1;   // Line 2 

Vì không có câu trả lời cụ thể cho phần này của câu hỏi đó, và tôi muốn biết hành vi trong C++, tôi nâng nó một lần nữa ở đây.


Rule # 2 từ Undefined Behavior and Sequence Points nói

Bên cạnh đó, giá trị trước khi được truy cập duy nhất để xác định giá trị được lưu trữ

Rõ ràng trong ví dụ trên, giá trị là được truy cập hai lần: a[i] (lhs) và i (rhs), và chỉ một trong số chúng (số rh) xác định giá trị được lưu trữ.

Dòng 2 có vi phạm quy tắc ở trên và dẫn đến hành vi không xác định trong C++ 03 không?


Có một số nhầm lẫn về việc liệu i có được sửa đổi ở Dòng 2 không?

Yes it is modified!

Trả lời

18

Điều này sẽ dẫn đến hành vi không xác định trong C++ 03 và hành vi được xác định rõ trong C++ 11.

C++ 03: Không xác định Behvaior

Từ tiêu chuẩn C++ 03, phần 5 đoạn 4:

Giữa điểm chuỗi trước và bên cạnh một đối tượng vô hướng có trách nhiệm lưu trữ của nó giá trị được sửa đổi nhiều nhất một lần bằng cách đánh giá biểu thức. Hơn nữa, giá trị trước chỉ được truy cập để xác định giá trị được lưu trữ.

Lưu ý câu thứ hai: Giá trị trước đó của i chỉ có thể được sử dụng để xác định giá trị được lưu trữ. Nhưng ở đây nó cũng được sử dụng để xác định chỉ số mảng. Do bài tập này sẽ sửa đổi i, a[0] = i+1 được xác định rõ, trong khi a[i] = i+1 thì không. Lưu ý rằng nhiệm vụ không tạo ra một điểm chuỗi: chỉ có kết thúc của biểu thức đầy đủ (dấu chấm phẩy).


C++ 11: Lộ rõ hành vi:

C++ 11 đã thoát khỏi những khái niệm về điểm chuỗi, và thay vào đó xác định mà đánh giá được sắp xếp trước đó.

Từ tiêu chuẩn, phần 1.9 Đoạn 15:

Các tính toán giá trị của toán hạng của một nhà điều hành được sắp xếp trước khi tính toán giá trị của các kết quả của các nhà điều hành. Nếu một tác dụng phụ trên một đối tượng vô hướng không liên quan đến một hiệu ứng phụ khác trên cùng một đối tượng vô hướng hoặc tính toán giá trị bằng cách sử dụng giá trị của cùng một đối tượng vô hướng, hành vi là không xác định.

Cả toán hạng của toán tử gán đều được sắp xếp trước khi gán thực tế. Vì vậy, cả hai a[i]i+1 sẽ được đánh giá, và chỉ sau đó sẽ i được sửa đổi. Kết quả được xác định rõ.

+4

+ 1 cho cả C + +03 và C++ 11 câu trả lời. –

3

int i = 0, *a = &i;

có một điểm chuỗi giữa tờ khai, do đó không có UB đây. Tuy nhiên, hãy lưu ý rằng bạn nên khai báo/xác định biến theo cách đó. Bất kỳ chuẩn mã hóa bình thường nào cũng sẽ cho bạn biết khai báo một biến trên mỗi dòng.

a[i] = i;

Các i không được thay đổi trong bất kỳ cách nào, do đó không có UB ở đây cả.

+1

Còn về 'a [i] = i + 1; 'thì sao? – Lazer

+2

@Lazer: 'i + 1' không thay đổi' i' hoặc là –

+0

@Lazer Vẫn không, bởi vì 'i' vẫn chưa được sửa đổi. –

0

Hành vi không xác định trong trường hợp này sẽ chỉ diễn ra nếu bạn sửa đổi cùng một địa chỉ bộ nhớ mà không có một điểm chuỗi giữa các sửa đổi. Cụ thể, thông số C99, phần 6.5/2,

Giữa điểm trước đó và tiếp theo đối tượng phải có giá trị được lưu giữ được sửa đổi nhiều nhất một lần bằng cách đánh giá biểu thức. Hơn nữa, giá trị trước chỉ được truy cập để xác định giá trị cần lưu trữ.

Trong trường hợp của bạn, không có sửa đổi cùng một địa chỉ bộ nhớ diễn ra giữa các điểm chuỗi, do đó không có hành vi không xác định.

+2

Ở đây 'i' đang được sửa đổi mà không có điểm chuỗi (dấu chấm phẩy là điểm chuỗi duy nhất trong Dòng 2 tôi nghĩ) – Lazer

0

Tôi muốn chỉ ra một điều: a[i] = ikhông luôn dẫn đến hành vi được xác định rõ.Lý do tại sao hành vi được xác định rõ trong trường hợp được chỉ định, là do các giá trị ban đầu ia.

Hãy để tôi giải thích:

int i = 1, *a = &i; // Line 1, i initialized to anything other than 0 
a[i] = i + 1;   // Line 2, all of a sudden we are in buffer over/underflow 

Đối với bất kỳ giá trị ban đầu khác của i chúng ta đang truy cập vào một vị trí bộ nhớ khác với i chính nó, trong đó sản xuất hành vi không xác định.

+0

Thực tế khi' a' trỏ đến một số nguyên 'i', không quan trọng giá trị của' i' là, 'a [i] = i' luôn là hành vi không xác định (nếu' * (& i + i) = i' là UB, tức là, theo câu trả lời của interjay, nó là) –

2

Hãy để chúng tôi phân tích biểu thức a[i] = i + 1 bạn sẽ?

= -- [] -- a 
    \  \_ i 
    \ 
    \_ + -- i 
     \_ 1 

hiệu quả, a[i] đề cập đến &i tuy nhiên lưu ý rằng không phải a[i] cũng không i+1 đổi i. i chỉ được sửa đổi khi = (bản thân bài tập) được thi hành.

Kể từ khi toán hạng của bất kỳ chức năng cần được đánh giá trước chức năng này có hiệu lực, điều này thực sự là tương đương với:

void assign(int& address, int value) { address = value; } 

assign(a[i], i + 1); 

Đúng là = là hơi đặc biệt ở chỗ nó được xây dựng-in và không kết quả trong một cuộc gọi hàm, vẫn đánh giá cả hai toán hạng là được sắp xếp trước nhiệm vụ thực tế, vì vậy chúng được đánh giá trước tiên trước i đang được sửa đổi và a[i] (trỏ đến i vị trí) đang được gán.

+0

Điều gì về câu trả lời của interjay nói rằng giá trị trước đó chỉ có thể được truy cập để xác định giá trị được lưu trữ? –

+0

Đây là cách mọi thứ hoạt động trong C++ 11, nhưng không phải C++ 03 (mặc dù bất kỳ trình biên dịch C++ 03 hợp lý nào cũng có thể thực hiện mọi thứ theo cách này). Xem câu trả lời cập nhật của tôi. – interjay

+0

@interjay: ah đúng, tôi đã không bao giờ được quá hiểu biết về các điểm chuỗi vì vậy tôi chỉ dựa trên câu trả lời của tôi về tiêu chuẩn mới nhất. Câu trả lời hay, cảm ơn vì đã kỹ lưỡng. –

0

Không, không. Dòng đầu tiên có một điểm chuỗi (dấu phẩy), vì vậy nó không phải là hành vi không xác định:

int i = 0, *a = &i; 

Dòng thứ hai là hoàn toàn bình thường.

a[i] = i + 1; 

Kể từ i + 1 tạo ra một giá trị tạm thời, i được sửa đổi một lần duy nhất, về việc chuyển nhượng. Tuy nhiên, hành vi này sẽ không được xác định:

a[i] = i++; 
Các vấn đề liên quan