Подтвердить что ты не робот

С++ Можно ли определить, указывает ли указатель на действительный объект?

Я изучаю C++ и читаю C++ Primer. Есть вопрос, на который я бы хотел узнать ответ:

Имея указатель p, вы можете определить, указывает ли p на действительный объект? Если так, то как? Если нет, то почему нет?

4b9b3361

Ответ 1

Нет, вы не можете. Зачем? Потому что было бы дорого поддерживать метаданные о том, что представляет собой действительный указатель, а что нет, а на С++ вы не платите за то, что не хотите.

И вы не хотите проверять, является ли указатель действительным, потому что вы знаете, откуда приходит указатель, либо потому, что он является частной частью вашего кода, который вы контролируете, либо потому, что вы указали его в своих внешних контрактах.

Ответ 2

Невозможно. Подумайте об этом сценарии.

int *ptr = new int(10);
int *ptrDup = ptr;

delete ptr;

Но ptrDup все еще указывает на ячейку памяти, на которую указывает ptr, которая больше не существует. Таким образом, отсрочка ptrDup приводит к поведению undefined. Но есть подсчет ссылок, который является совершенно другой концепцией.

Ответ 3

Невозможно увидеть, является ли указатель "действительным" во всех его значениях.

Конечно, вы можете попытаться разыменовать указатель (*ptr = x; или x = *ptr). Если ваш код не сбой, указатель указывает на действительную память. Если он упал, очевидно, указатель не годится. К сожалению, этот подход немного похож на проверку того, загружена ли пушка, выстрелив в голову - что не самое умное... К сожалению, с указателями нет "проверьте камеру, чтобы увидеть, загружена ли она", поэтому нет реального хорошего способа выяснить, является ли указатель действительным, кроме "если он не вызывает аппаратную ошибку, он действителен".

Обратите внимание, что это только на самом деле скажет вам, что "указатель указывает на некоторую память, к которой вы можете получить доступ" в большинстве случаев. Это НЕ означает, что указатель "правильный для того, что вы хотите" (например, указывает на правильный тип). И он НЕ ТОЛЬКО не скажет вам, указывает ли указатель на "устаревшие данные" (то есть, когда указатель WAS действителен, но теперь он используется для чего-то еще).

К сожалению, с 2 32 или 2 64 [фактически 2 48] возможно действительными адресами памяти в современной системе, почти невозможно знать, какие адреса действительны, а какие нет. Даже внутри операционной системы, как показывает ОС, если она может записать в память, которую вы попросили написать, это "попытаться написать ее, посмотреть, что произойдет". Для ОС это работает отлично, потому что это может быть осторожным, "это может пойти не так, и если это произойдет, я продолжу там в коде восстановления ошибок". ОС должна справиться с этим, потому что она должна принять, а) что программисты совершают ошибки, и б) что некоторые люди на самом деле пишу вредоносный код для TRY, чтобы разорвать ОС.

Способ для приложения "удостовериться, что указатели действительны" - это то, что программист пишет код, который ОСТОРОЖНО, о том, что он хранит в указателях, как он освобождает эти указатели и использует только указатели, в которых хранятся действительные значения. Вы не должны заканчивать "необходимость проверки правильности указателя" - тогда вы "делаете это неправильно".

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

Конечно, в С++ существуют интеллектуальные указатели, векторы и различные другие инструменты, которые означают много времени, когда вам даже не нужно беспокоиться о указателях. Но понимание того, как использовать указатели и как работают указатели, по-прежнему хорошо.

Ответ 4

Если указатель установлен на nullptr, это означает, что ему не был дан объект, на который он указывает, и вместо этого ему присваивается значение по умолчанию. Возможно, что указатель не может быть привязан к nullptr и, но не будет назначен действительному объекту, но в этом случае определить это невозможно. Например:

С nullptr:

int *ptr = nullptr;

// check if pointer is unassigned to an object
if (ptr == nullptr) ptr = new int{0};

Без nullptr:

int *ptr;

// using ptr while uninitialized is Undefined Behavior!
if (ptr != &some_object)

Ответ 5

Как указано в других ответах, это невозможно с необработанным указателем формы SomeObject* somePointer. Однако в c++11 появился новый набор динамического управления памятью и новые интеллектуальные указатели. Используя интеллектуальный указатель, вы можете определить, доступен ли ресурс. Например, в следующем:

std::weak_ptr<int> w; // Our pointer to a resource.
{
    std::shared_pointer<int> s = std::make_shared<int>(5); // The resource.

    w = s;       // We can set the weak pointer to the shared pointer.

    auto s2 = w; // Here we can promote the weak pointer to a shared  pointer to control 
                 // the resource.

    *s2 = 6;     // Here we can use the resource.

}                // Here the resource is destroyed.

auto s2 = w;     // Here we will fail to get the resource because it has been destroyed. We
                 // have successfully used smart pointers to detect if the resource exists.

Подробнее о std :: shared_ptr и std :: weak_ptr для получения дополнительных примеров. До того, как c++11 в boost будут доступны эквивалентные типы интеллектуальных указателей.

Ответ 6

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

Однако различные компании разрабатывают библиотеки и инструменты для добавления кода для проверки каждого доступа к памяти, и, если он недействителен, вы получаете прерывание.

В g++ я использую параметры дезинфицирующего средства, например:

g++ -fsanitize=address -fsanitize=enum -fsanitize=unreachable ...

Первый из них защищает доступ к вашей памяти до такой степени, что попытка использовать неправильный указатель будет обнаружена с помощью SEGV. Он использует MMU для защиты вашей памяти, поэтому он аппаратно управляемый. Это замедляет ваш код, но все равно довольно быстро. В этом режиме следует обратить внимание на то, что двоичные файлы выделяют 2 ТБ виртуальной памяти. Вы не хотите запускать слишком много таких двоичных файлов одновременно, если у вас недостаточно ОЗУ.

В качестве примечания: часть кода взята из Google, а первая реализация была в Clang.

В прямом C/C++ под Linux вы можете проверить, находится ли указатель в вашем процессе. Тем не менее, при поддержке большого объема памяти это не удастся, и вы также должны учитывать стек. Начальный указатель - что-то вроде 0x400000. Конечный адрес кучи можно определить с помощью sbrk(). Таким образом, ваши указатели кучи должны быть между этими двумя границами.