Tất cả các hành vi không xác định phổ biến mà một lập trình viên C ++ nên biết là gì?
Nói, như:
a[i] = i++;
Tất cả các hành vi không xác định phổ biến mà một lập trình viên C ++ nên biết là gì?
Nói, như:
a[i] = i++;
Câu trả lời:
NULL
con trỏmemcpy
để sao chép bộ đệm chồng chéo .int64_t i = 1; i <<= 72
không xác định)int i; i++; cout << i;
:)volatile
hoặc sig_atomic_t
khi nhận tín hiệulong int
#if
biểu thứcThứ tự mà các tham số chức năng được ước tính là hành vi không xác định . (Điều này sẽ không làm cho chương trình của bạn bị sập, phát nổ hoặc đặt mua pizza ... không giống như hành vi không xác định .)
Yêu cầu duy nhất là tất cả các tham số phải được đánh giá đầy đủ trước khi hàm được gọi.
Điều này:
// The simple obvious one.
callFunc(getA(),getB());
Có thể tương đương với điều này:
int a = getA();
int b = getB();
callFunc(a,b);
Hoặc này:
int b = getB();
int a = getA();
callFunc(a,b);
Nó có thể là một trong hai; tùy thuộc vào trình biên dịch. Kết quả có thể quan trọng, tùy thuộc vào tác dụng phụ.
Trình biên dịch có thể tự do sắp xếp lại các phần đánh giá của một biểu thức (giả sử ý nghĩa là không thay đổi).
Từ câu hỏi ban đầu:
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
Kiểm tra khóa kép. Và một sai lầm dễ mắc phải.
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
Sở thích của tôi là "đệ quy vô hạn trong việc khởi tạo các mẫu" bởi vì tôi tin rằng đó là lần duy nhất có hành vi không xác định xảy ra tại thời điểm biên dịch.
Bên cạnh hành vi không xác định , còn có hành vi được xác định thực hiện khó chịu không kém .
Hành vi không xác định xảy ra khi một chương trình làm một cái gì đó mà kết quả không được chỉ định bởi tiêu chuẩn.
Hành vi xác định thực hiện là một hành động của một chương trình mà kết quả không được xác định bởi tiêu chuẩn, nhưng việc thực hiện được yêu cầu phải ghi lại. Một ví dụ là "Multibyte ký tự chữ", từ câu hỏi Stack Overflow Có trình biên dịch C không biên dịch được điều này không?.
Hành vi được xác định theo triển khai chỉ cắn bạn khi bạn bắt đầu chuyển (nhưng nâng cấp lên phiên bản mới của trình biên dịch cũng đang chuyển!)
Các biến chỉ có thể được cập nhật một lần trong một biểu thức (về mặt kỹ thuật một lần giữa các điểm chuỗi).
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
Một sự hiểu biết cơ bản về các giới hạn môi trường khác nhau. Danh sách đầy đủ nằm trong mục 5.2.4.1 của thông số kỹ thuật C. Ở đây có một ít;
Tôi thực sự hơi ngạc nhiên khi giới hạn 1023 trường hợp nhãn cho câu lệnh chuyển đổi, tôi có thể thấy rằng việc vượt quá đối với mã / lex / trình phân tích cú pháp được tạo ra khá dễ dàng.
Nếu các giới hạn này bị vượt quá, bạn có hành vi không xác định (sự cố, lỗi bảo mật, v.v ...).
Phải, tôi biết đây là từ đặc tả C, nhưng C ++ chia sẻ những hỗ trợ cơ bản này.
Sử dụng memcpy
để sao chép giữa các vùng bộ nhớ chồng chéo. Ví dụ:
char a[256] = {};
memcpy(a, a, sizeof(a));
Hành vi không được xác định theo Tiêu chuẩn C, được áp dụng theo Tiêu chuẩn C ++ 03.
Tóm tắc
1 / #include void * memcpy (void * hạn chế s1, const void * hạn chế s2, size_t n);
Sự miêu tả
2 / Hàm memcpy sao chép n ký tự từ đối tượng được trỏ bởi s2 vào đối tượng được trỏ bởi s1. Nếu sao chép diễn ra giữa các đối tượng chồng lấp, hành vi không được xác định. Trả về 3 Hàm memcpy trả về giá trị của s1.
Tóm tắc
1 #incolee void * memmove (void * s1, const void * s2, size_t n);
Sự miêu tả
2 Hàm memmove sao chép n ký tự từ đối tượng được trỏ bởi s2 vào đối tượng được trỏ bởi s1. Việc sao chép diễn ra như thể các ký tự n từ đối tượng được trỏ bởi s2 trước tiên được sao chép vào một mảng tạm thời của n ký tự không trùng với các đối tượng được trỏ bởi s1 và s2, sau đó các ký tự n từ mảng tạm thời được sao chép vào đối tượng được chỉ bởi s1. Trả về
3 Hàm memmove trả về giá trị của s1.
Loại duy nhất mà C ++ đảm bảo kích thước là char
. Và kích thước là 1. Kích thước của tất cả các loại khác phụ thuộc vào nền tảng.