C ++ 98 và C ++ 03
Câu trả lời này dành cho các phiên bản cũ hơn của tiêu chuẩn C ++. Các phiên bản C ++ 11 và C ++ 14 của tiêu chuẩn không chính thức chứa 'điểm chuỗi'; thay vào đó, các hoạt động được 'giải trình tự trước' hoặc 'không có kết quả' hoặc 'trình tự không xác định'. Hiệu ứng ròng về cơ bản là giống nhau, nhưng thuật ngữ là khác nhau.
Tuyên bố từ chối trách nhiệm : Được rồi. Câu trả lời này hơi dài. Vì vậy, hãy kiên nhẫn trong khi đọc nó. Nếu bạn đã biết những điều này, đọc lại chúng sẽ không làm bạn phát điên.
Điều kiện tiên quyết : Một kiến thức cơ bản về Tiêu chuẩn C ++
Điểm trình tự là gì?
Tiêu chuẩn nói
Tại một số điểm nhất định trong chuỗi thực hiện được gọi là điểm chuỗi , tất cả các tác dụng phụ của các đánh giá trước sẽ được hoàn thành và không có tác dụng phụ của các đánh giá tiếp theo sẽ xảy ra. (§1.9 / 7)
Phản ứng phụ? Tác dụng phụ là gì?
Đánh giá một biểu thức tạo ra một cái gì đó và nếu ngoài ra có sự thay đổi trạng thái của môi trường thực thi thì người ta nói rằng biểu thức (đánh giá của nó) có một số tác dụng phụ.
Ví dụ:
int x = y++; //where y is also an int
Ngoài hoạt động khởi tạo, giá trị của y
được thay đổi do tác dụng phụ của ++
toán tử.
Càng xa càng tốt. Chuyển sang điểm chuỗi. Một định nghĩa xen kẽ các điểm seq được đưa ra bởi tác giả comp.lang.c Steve Summit
:
Điểm tuần tự là thời điểm mà bụi đã lắng xuống và tất cả các tác dụng phụ đã được nhìn thấy cho đến nay được đảm bảo hoàn thành.
Các điểm trình tự phổ biến được liệt kê trong Tiêu chuẩn C ++ là gì?
Những người đang có:
ở cuối đánh giá biểu thức đầy đủ ( §1.9/16
) (Biểu thức đầy đủ là biểu thức không phải là biểu thức con của biểu thức khác.) 1
Thí dụ :
int a = 5; // ; is a sequence point here
trong việc đánh giá từng biểu thức sau sau khi đánh giá biểu thức đầu tiên ( §1.9/18
) 2
a && b (§5.14)
a || b (§5.15)
a ? b : c (§5.16)
a , b (§5.18)
(ở đây a, b là toán tử dấu phẩy; func(a,a++)
,
không phải là toán tử dấu phẩy, nó chỉ là dấu phân cách giữa các đối số a
và a++
. Do đó, hành vi không được xác định trong trường hợp đó (nếu a
được coi là kiểu nguyên thủy))
tại một lệnh gọi hàm (có hay không hàm là nội tuyến), sau khi đánh giá tất cả các đối số hàm (nếu có) diễn ra trước khi thực hiện bất kỳ biểu thức hoặc câu lệnh nào trong thân hàm ( §1.9/17
).
1: Lưu ý: việc đánh giá biểu thức đầy đủ có thể bao gồm việc đánh giá các biểu thức con không phải là một phần từ vựng của biểu thức đầy đủ. Ví dụ: các biểu thức con liên quan đến việc đánh giá các biểu thức đối số mặc định (8.3.6) được coi là được tạo trong biểu thức gọi hàm, không phải là biểu thức xác định đối số mặc định
2: Các toán tử được chỉ định là các toán tử dựng sẵn, như được mô tả trong điều 5. Khi một trong các toán tử này bị quá tải (mệnh đề 13) trong ngữ cảnh hợp lệ, do đó chỉ định một hàm toán tử do người dùng định nghĩa, biểu thức chỉ định một lệnh gọi hàm và các toán hạng tạo thành một danh sách đối số, không có điểm thứ tự ngụ ý giữa chúng.
Hành vi không xác định là gì?
Tiêu chuẩn định nghĩa Hành vi không xác định trong Phần §1.3.12
là
hành vi, chẳng hạn như có thể phát sinh khi sử dụng cấu trúc chương trình sai hoặc dữ liệu sai, mà Tiêu chuẩn quốc tế này áp đặt không có yêu cầu 3 .
Hành vi không xác định cũng có thể được dự kiến khi Tiêu chuẩn quốc tế này bỏ qua mô tả về bất kỳ định nghĩa rõ ràng nào về hành vi.
3. (với việc phát hành một thông điệp chẩn đoán).
Nói tóm lại, hành vi không xác định có nghĩa là bất cứ điều gì có thể xảy ra từ daemon bay ra khỏi mũi của bạn để bạn gái của bạn mang thai.
Mối quan hệ giữa Hành vi không xác định và Điểm trình tự là gì?
Trước khi tôi hiểu rằng bạn phải biết (các) sự khác biệt giữa Hành vi không xác định, Hành vi không xác định và Hành vi được xác định thực hiện .
Bạn cũng phải biết rằng the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified
.
Ví dụ:
int x = 5, y = 6;
int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.
Một ví dụ khác ở đây .
Bây giờ Tiêu chuẩn §5/4
nói
- 1) Giữa điểm thứ tự trước và điểm tiếp theo, một đối tượng vô hướng sẽ có giá trị được lưu trữ được sửa đổi nhiều nhất một lần bằng cách đánh giá biểu thức.
Nó có nghĩa là gì?
Một cách không chính thức có nghĩa là giữa hai điểm chuỗi, một biến không được sửa đổi nhiều lần. Trong một tuyên bố biểu thức, next sequence point
thường là ở dấu chấm phẩy kết thúc và previous sequence point
ở cuối câu lệnh trước. Một biểu thức cũng có thể chứa trung gian sequence points
.
Từ câu trên, các biểu thức sau đây gọi Hành vi không xác định:
i++ * ++i; // UB, i is modified more than once btw two SPs
i = ++i; // UB, same as above
++i = 2; // UB, same as above
i = ++i + 1; // UB, same as above
++++++i; // UB, parsed as (++(++(++i)))
i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)
Nhưng các biểu thức sau đây là tốt:
i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i); // well defined
int j = i;
j = (++i, i++, j*i); // well defined
- 2) Hơn nữa, giá trị trước chỉ được truy cập để xác định giá trị sẽ được lưu trữ.
Nó có nghĩa là gì? Nó có nghĩa là nếu một đối tượng được ghi vào trong một biểu thức đầy đủ, bất kỳ và tất cả các truy cập vào nó trong cùng một biểu thức phải được tham gia trực tiếp vào tính toán của giá trị được viết .
Ví dụ, trong i = i + 1
tất cả các truy cập của i
(trong LHS và trong RHS) đều liên quan trực tiếp đến việc tính toán giá trị được viết. Vậy là ổn rồi.
Quy tắc này hạn chế một cách hiệu quả các biểu thức pháp lý đối với những người truy cập trước khi sửa đổi.
Ví dụ 1:
std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2
Ví dụ 2:
a[i] = i++ // or a[++i] = i or a[i++] = ++i etc
không được phép vì một trong những quyền truy cập của i
(một trong a[i]
) không liên quan gì đến giá trị cuối cùng được lưu trữ trong i (xảy ra trong i++
) và vì vậy không có cách nào tốt để xác định - theo cách hiểu của chúng tôi hoặc trình biên dịch - cho dù việc truy cập nên diễn ra trước hay sau khi giá trị gia tăng được lưu trữ. Vì vậy, hành vi là không xác định.
Ví dụ 3:
int x = i + i++ ;// Similar to above
Theo dõi câu trả lời cho C ++ 11 tại đây .
*p++ = 4
không xác định hành vi.*p++
được hiểu là*(p++)
.p++
trả vềp
(một bản sao) và giá trị được lưu trữ tại địa chỉ trước đó. Tại sao điều đó sẽ gọi UB? Nó là hoàn toàn tốt.