Я читаю Разрешены ли отрицательные индексы массива в C? и нашел интересным, что отрицательные значения могут быть использованы для индекса массива. Я попробовал это снова с C ++ 11 unique_ptr
и это работает там же! Конечно, средство удаления должно быть заменено чем-то, что может удалить исходный массив. Вот как это выглядит:
#include <iostream>
#include <memory>
int main()
{
const int min = -23; // the smaller valid index
const int max = -21; // the highest valid index
const auto deleter = [min](char* p)
{
delete [](p+min);
};
std::unique_ptr<char[],decltype(deleter)> up(new char[max-min+1] - min, deleter);
// this works as expected
up[-23] = 'h'; up[-22] = 'i'; up[-21] = 0;
std::cout << (up.get()-23) << '\n'; // outputs:hi
}
Мне интересно, есть ли очень, очень маленький шанс, что есть утечка памяти. Адрес памяти созданной в куче (new char[max-min+1]
) может переполниться при добавлении 23 к нему и стать нулевым указателем. Вычитание 23 по-прежнему дает исходный адрес массива, но unique_ptr
может распознать его как нулевой указатель. unique_ptr
не может удалить его, потому что это нуль.
Итак, есть ли вероятность того, что предыдущий код протечет память или умный указатель ведет себя так, что это делает его безопасным?
Примечание: я бы на самом деле не использовал это в реальном коде; Мне просто интересно, как это будет вести себя.
Редактировать: пузырь со льдом В связи с этим возникает интересный момент: в арифметике указателей допустимы только два допустимых значения указателя:
§5.7 [expr.add] p5
Если оба указателя операнд и результат При указании на элементы одного и того же объекта массива или одного элемента после последнего элемента объекта массива оценка не должна вызывать переполнения; в противном случае поведение не определено.
Таким образом, new char[N] - min
вашего кода уже вызывает UB.
Теперь на большинстве реализаций это не вызовет проблем. Деструктор std::unique_ptr
однако (предварительно отредактируйте ответ отсюда):
§20.7.1.2.2 [unique.ptr.single.dtor] p2
Эффекты: если
get() == nullptr
нет никаких эффектов. Иначеget_deleter()(get())
,
Так что да, есть вероятность, что вы потеряете память здесь, если она действительно сопоставляется с любым значением, представляющим значение нулевого указателя (скорее всего 0
, но не обязательно). И да, я знаю, что это один для отдельных объектов, но массив один ведет себя точно так же:
§20.7.1.3 [unique.ptr.runtime] p2
Описания приведены ниже только для функций-членов, поведение которых отличается от основного шаблона.
И нет описания для деструктора.
new char[max-min+1]
не выделяет память в стеке, а скорее в куче — вот как это стандартно operator new
ведет себя. Выражение max-min+1
оценивается компилятором и приводит к 3
таким образом, в конечном итоге это выражение равно выделению 3 байтов в куче. Здесь нет проблем.
Тем не менее, вычитая min
приводит к указателю, который на 23 байта превышает начало выделенной памяти, возвращенной new
и поскольку в new вы выделили только 3 байта, это определенно будет указывать на местоположение, не принадлежащее вам -> все последующее приведет к неопределенному поведению.