Каковы все общие неопределенные поведения, о которых должен знать программист на C ++?
Скажите, например:
a[i] = i++;
Каковы все общие неопределенные поведения, о которых должен знать программист на C ++?
Скажите, например:
a[i] = i++;
NULL
memcpy
для копирования перекрывающихся буферов.int64_t i = 1; i <<= 72
не определено)int i; i++; cout << i;
)volatile
или sig_atomic_t
при получении сигналаlong int
#if
sizeof(int) * CHAR_BIT <= 72
истинно. Объект, в который вы помещаете результат 37 << 72
, не имеет значения, само выражение вызывает неопределенное поведение. Например, uint8_t n = (1 << 15);
прекрасно. Сначала вы оцениваете 1 << 15
, что составляет 2 ** 15. Результатом является затем неявное приведение от int
к uint8_t
, что означает, что n == 0
(из-за модульной арифметики)
- person David Stone; 21.02.2013
Порядок оценки параметров функции - неопределенное поведение. (Это не приведет к сбою, взрыву программы или заказу пиццы ... в отличие от неопределенного поведения.)
Единственное требование - все параметры должны быть полностью вычислены перед вызовом функции.
Этот:
// 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.
=
должны быть оценены, прежде чем его можно будет оценить.
- person Martin York; 12.10.2010
a[i] = i++;
в любом случае является неопределенным поведением. Компилятор может делать больше, чем просто переупорядочивать его, он может игнорировать это или делать что-то с носом. i
модифицируется на RHS, а на LHS он читается иначе, чем с целью определения записываемого значения. Игра окончена (если i
не является типом класса с operator++
перегрузкой, тогда любые модификации объекта внутри этой перегрузки надежно обертываются в точках последовательности, и это просто неуказанный порядок, а не UB).
- person Steve Jessop; 07.06.2011
i
- это тип класса, который имитирует целое число с подходящими operator++(int)
и operator int()
, ваши два варианта "либо / или" все еще возможны, но нет UB. Тем не менее, возможно, говоря о неопределенном поведении, спрашивающий имеет в виду любую ситуацию, в которой поведение не полностью закреплено, в отличие от стандартного определения UB.
- person Steve Jessop; 07.06.2011
Мне больше всего нравится «Бесконечная рекурсия при создании экземпляров шаблонов», потому что я считаю, что это единственный вариант, в котором неопределенное поведение возникает во время компиляции.
Назначение константе после удаления const
ness с помощью const_cast<>
:
const int i = 10;
int *p = const_cast<int*>( &i );
*p = 1234; //Undefined
Помимо неопределенного поведения, существует также не менее неприятное поведение, определяемое реализацией.
Неопределенное поведение возникает, когда программа делает что-то, результат чего не определен стандартом.
Поведение, определяемое реализацией - это действие программы, результат которого не определен стандартом, но который реализация должна задокументировать. Примером могут служить «Многобайтовые символьные литералы» из вопроса о переполнении стека Есть ли компилятор C, который не может это скомпилировать? < / em>.
Поведение, определяемое реализацией, укусит вас только тогда, когда вы начнете переносить (но обновление до новой версии компилятора также переносится!)
Переменные могут быть обновлены только один раз в выражении (технически один раз между точками последовательности).
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
Базовое понимание различных экологических ограничений. Полный список находится в разделе 5.2.4.1 спецификации C. Вот несколько;
На самом деле я был немного удивлен ограничением в 1023 меток case для оператора switch, я могу предвидеть, что оно будет превышено для сгенерированного кода / lex / парсеров довольно легко.
Если эти ограничения превышены, у вас неопределенное поведение (сбои, недостатки безопасности и т. Д.).
Да, я знаю, что это из спецификации C, но C ++ разделяет эту базовую поддержку.
Использование memcpy
для копирования между перекрывающимися областями памяти. Например:
char a[256] = {};
memcpy(a, a, sizeof(a));
Поведение не определено в соответствии со стандартом C, который входит в стандарт C ++ 03.
Синопсис
1 / #include void * memcpy (void * restrict s1, const void * restrict s2, size_t n);
Описание
2 / Функция memcpy копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. Если копирование происходит между перекрывающимися объектами, поведение не определено. Возвращает 3 Функция memcpy возвращает значение s1.
Синопсис
1 # включить void * memmove (void * s1, const void * s2, size_t n);
Описание
2 Функция memmove копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. Копирование происходит так, как если бы n символов из объекта, на который указывает s2, сначала копируются во временный массив из n символов, который не перекрывает объекты, на которые указывают s1 и s2, а затем n символов из временного массива копируются в объект, на который указывает s1. Возврат
3 Функция memmove возвращает значение s1.
Единственный тип, для которого C ++ гарантирует размер - char
. И размер равен 1. Размер всех остальных типов зависит от платформы.
int
составляет не менее 16 бит, а long long
- не менее 64 (добавлено в стандарт в C ++ 11).
- person David Stone; 21.02.2013
Объекты уровня пространства имен в разных единицах компиляции никогда не должны зависеть друг от друга при инициализации, поскольку их порядок инициализации не определен.