Каковы все общие поведения undefined, о которых должен знать программист на С++?
Скажите, например:
a[i] = i++;
Каковы все общие поведения undefined, о которых должен знать программист на С++?
Скажите, например:
a[i] = i++;
NULL
memcpy
для копирования перекрывающихся буферов.int64_t i = 1; i <<= 72
равно undefined)int i; i++; cout << i;
)volatile
или sig_atomic_t
при получении сигналаlong int
#if
Порядок оценки параметров функции - это неуказанное поведение. (Это не приведет к сбою, взрыву программы или заказу пиццы... в отличие от поведения undefined.)
Единственное требование состоит в том, чтобы все параметры должны быть полностью оценены до вызова функции.
Это:
// The simple obvious one.
callFunc(getA(),getB());
Может быть эквивалентно этому:
int a = getA();
int b = getB();
callFunc(a,b);
Или это:
int b = getB();
int a = getA();
callFunc(a,b);
Это может быть либо; это до компилятора. Результат может иметь значение, в зависимости от побочных эффектов.
Компилятор может свободно переопределять оценочные части выражения (при условии, что значение не изменяется).
Из исходного вопроса:
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;
Двойная проверка блокировки. И сделать одну легкую ошибку.
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.
Назначение константы после удаления const
ness с использованием const_cast<>
:
const int i = 10;
int *p = const_cast<int*>( &i );
*p = 1234; //Undefined
Моим любимым является "Бесконечная рекурсия в создании шаблонов", потому что я считаю, что это единственный, где поведение undefined происходит во время компиляции.
Помимо undefined поведения, существует также такое же неприятное поведение, определяемое реализацией.
undefined поведение происходит, когда программа выполняет что-то, результат которого не указан стандартом.
Поведение, определяемое реализацией, является действием программы, результат которой не определен стандартом, но который требуется для документирования. Например, "Многобайтовые символьные литералы", из вопроса "Переполнение стека" Есть ли компилятор C, который не может скомпилировать это?.
Поведение, определяемое реализацией, только укусит вас при запуске переноса (но обновление до новой версии компилятора также переносит!)
Переменные могут обновляться только один раз в выражении (технически один раз между точками последовательности).
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
Основное понимание различных экологических ограничений. Полный список приведен в разделе 5.2.4.1 спецификации C. Вот несколько:
На самом деле я был немного удивлен предел 1023 case-ярлыков для оператора switch, я могу заметить, что это довольно простое сведение сгенерированного кода /lex/parsers.
Если эти лимиты превышены, у вас есть поведение undefined (сбои, недостатки безопасности и т.д.).
Правильно, я знаю, что это из спецификации C, но С++ разделяет эти основные поддержки.
Единственным типом, для которого С++ гарантирует размер, является char
. И размер равен 1. Размер всех других типов зависит от платформы.
Объекты уровня пространства имен в разных единицах компиляции никогда не должны зависеть друг от друга для инициализации, потому что их порядок инициализации undefined.
Использование memcpy
для копирования между перекрывающимися областями памяти. Например:
char a[256] = {};
memcpy(a, a, sizeof(a));
Поведение undefined в соответствии со стандартом C, которое подпадает под стандарт С++ 03.
Сводка
1/#include void * memcpy (void * restrict s1, const void * ограничивать s2, size_t n);
Описание
2/Функция memcpy копирует n символов из объекта, на который указывает s2, в объект на которое указывает s1. Если копирование происходит между перекрываемыми объектами, поведение undefined. Возвращает 3 Функция memcpy возвращает значение s1.
Сводка
1 #include void * memmove (void * s1, const void * s2, size_t п);
Описание
2 Функция memmove копирует n символов из объекта, на который указывает s2 в объект, на который указывает s1. Копирование происходит, как если бы n символов из объекта, на который указывает s2, сначала копируются в временный массив из n символов, которые не перекрывают объекты на которые указывают s1 и s2, а затем n символов из временного массив копируются в объект, на который указывает s1. Возвращает
3 Функция memmove возвращает значение s1.