Когда можно получить доступ к указателю на "мертвый" объект?


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

рассмотрим следующие два примера.

Пример 1

typedef struct { int *p; } T;

T a = { malloc(sizeof(int) };
free(a.p);  // a.p is now indeterminate?
T b = a;    // Access through a non-character type?

Пример 2

void foo(int *p) {}

int *p = malloc(sizeof(int));
free(p);   // p is now indeterminate?
foo(p);    // Access through a non-character type?

вопрос

любой из приведенных выше примеров вызывает неопределенное поведение?

контекст

этот вопрос, заданный в ответ на эту дискуссию. Этот предположение состояло в том, что, например, аргументы указателя могут быть переданы функции через сегментные регистры x86, что может вызвать аппаратное исключение.

из стандарта C99, мы узнаем следующее (выделено мной):

[3.17]неопределено значение - либо неопределенное значение, либо ловушка представление

и затем:

[6.2.4 p2] значение a указатель становится неопределено когда объект на который он указывает достигает конца своей жизни.

и затем:

[6.2.6.1 p5] некоторые представления объектов не должны представлять значение типа объекта. Если сохраненное значение объекта имеет такое представление и считывается выражением lvalue, которое не имеет символьного типа,поведение не определено. Если такое представление создается a побочный эффект, который изменяет все или любую часть объекта с помощью выражения lvalue, которое не имеет символьного типа, поведение не определено. Такое представление называется ловушка представление.

принимая все это вместе, какие ограничения у нас есть на доступ к указателям на "мертвые" объекты?

дополнительное соглашение

хотя я процитировал стандарт C99 выше, мне было бы интересно узнать, отличается ли поведение любой из стандартов C++.

3 53

3 ответа:

Пример 2 недопустим. Анализ в вашем вопросе правильный.

Пример 1 является действительным. Тип структуры никогда не содержит представление ловушки, даже если это делает один из его членов. Это означает, что назначение структуры в системе, где представления ловушек вызывают проблемы, должно быть реализовано как побочная копия, а не как копия по элементам.

6.2.6 представлений типа

6.2.6.1 Генерал

6 [...] Значение объекта структуры или объединения никогда не является t rap представление, даже если значение элемента структуры или объекта объединения может быть представление ловушки.

моя интерпретация заключается в том, что, хотя только несимвольные типы могут иметь представления trap, любой тип может иметь неопределенное значение, и что доступ к объекту с неопределенным значением каким-либо образом вызывает неопределенное поведение. Самым печально известным примером может быть недопустимое использование OpenSSL неинициализированных объектов в качестве случайного семени.

Итак, ответ на ваш вопрос будет: никогда.

кстати, интересное следствие не только заостренного объекта, но и указатель будучи неопределенным после free или realloc это то, что эта идиома вызывает неопределенное поведение:

void *tmp = realloc(ptr, newsize);
if (tmp != ptr) {
    /* ... */
}

C++ для обсуждения

короткий ответ: в C++ нет такой вещи, как доступ к "чтению" экземпляра класса; вы можете только "читать" неклассовый объект, и это делается путем преобразования lvalue-rvalue.

подробный ответ:

typedef struct { int *p; } T;

T определяет неименованный класс. Ради обсуждения, давайте назовем этот класс T:

struct T {
    int *p; 
};

поскольку вы не объявили конструктор копирования, компилятор неявно объявляет один, поэтому определение класса гласит:

struct T {
    int *p; 
    T (const T&);
};

Итак, мы имеем:

T a;
T b = a;    // Access through a non-character type?

да, действительно; это инициализация конструктором копирования, поэтому определение конструктора копирования будет сгенерировано компилятором; определение эквивалентно

inline T::T (const T& rhs) 
    : p(rhs.p) {
}

так вы получаете доступ к значению как указатель, а не кучка байтов.

если значение указателя является недопустимым (не инициализировано, освобождено), поведение не определен.