Может ли новое выражение «переполниться» в случае массива int? - PullRequest
0 голосов
/ 10 января 2019

Что гарантированно произойдет, если вы напишите int *p = new int[SIZE_MAX];? Исключение брошено? Или возможно, что базовая функция operator new[] вызывается с аргументом, вычисляемым как SIZE_MAX * sizeof(int), с возможным неконтролируемым переполнением (уменьшение по модулю)?

В C ++ 17 ( N4659 ) § 6.9.2 [basic.compound] ¶ 2:

Создание типа таким образом, чтобы число байтов в его объектном представлении превышало максимальное значение, представляемое в типе std::size_t (21.2), является некорректным.

Каковы последствия "плохо сформированного" типа? Неопределенное-поведение?

Предположим, sizeof(int) больше 1. Правильно ли сформирована следующая программа и гарантированно выдается исключение?

#include <cstdint> // SIZE_MAX
#include <cstddef> // std::size_t

int main() {
    std::size_t size_max = (SIZE_MAX);
    int *pointer = new int[size_max];
}

Или я должен выполнить следующее обнаружение переполнения?

#include <cstdint> // SIZE_MAX
#include <cstddef> // std::size_t
#include <new>     // std::bad_alloc

bool mul_overflow (std::size_t a, std::size_t b) {
    std::size_t size_max = (SIZE_MAX);
    return a > (size_max / b);
}

int main() {
    std::size_t size_max = (SIZE_MAX);
    if (mul_overflow (size_max, sizeof(int)))
        throw std::bad_alloc ();

    int *pointer = new int[size_max];
}

Цель состоит в том, чтобы избежать целочисленного переполнения в оптимизированной, но стандартной соответствующей реализации, которая может выполнять умножение без проверки на уменьшение по модулю, если стандарт говорит, что то, что происходит в этом случае, определяется реализацией (или неопределенным / неуказанным поведением ).

Примечания:

  • Я говорю «переполнение», хотя целочисленные типы без знака не переполняются. Это означает «уменьшение по модулю» в этом посте.
  • new-expression определено в C ++ 17 ( N4659 ) § 8.3.4 [expr.new] ¶ 1.
  • Это вопрос «языкового адвоката».

1 Ответ

0 голосов
/ 10 января 2019

В цитируемом вами абзаце говорится о типах .

т.е. Вы не можете иметь тип T с sizeof(T) > SIZE_MAX. Это не относится к new и должно быть диагностировано компилятором на этапе компиляции.

Поведение new объясняется в другом месте. Начиная с C ++ 11, существует специальный тип исключения std::bad_array_new_length, который реализация должна выдавать, если размер ошибочен. Вот соответствующая цитата из стандарта:

[expr.new]

Выражение в объявлении-noptr-new является ошибочным, если:

  • (8.1) выражение имеет неклассный тип, и его значение до преобразования в std :: size_t меньше нуля;
  • (8.2) выражение относится к типу класса и его значение до применения второго стандартного преобразования ([over.ics.user]) меньше ноль;
  • (8.3) его значение таково, что размер выделенного объекта будет превышать предел, определенный реализацией; или
  • (8.4) new-initializer представляет собой список фигурных скобок init и список элементов массива, для которых предоставляются инициализаторы (включая заканчивающийся '\ 0' в строковом литерале) превышает количество элементов инициализировать.

Если выражение является ошибочным после преобразования в std :: size_t:

  • (8.6.1) если функция распределения, которая была бы вызвана, имеет спецификацию исключений, не генерирующую исключение ([exc.spec]), значение new-выражение - это значение нулевого указателя требуемого типа результата;
  • (8.6.2) в противном случае выражение new завершается, генерируя исключение типа, который бы соответствовал обработчику ([exc.handle]) из Тип std :: bad_array_new_length.

Так что это ожидаемое поведение. Однако имейте в виду, что ваша перегрузка operator new будет получать только окончательные значения size_t (т. Е. После всех вычислений), поэтому вам не нужно делать / не выполнять уменьшение по модулю.

При реализации собственного operator new (8.5) должен заботиться компилятор, и вам нужно только позаботиться о (8.6).

...