Может ли static_cast превратить ненулевой указатель в нулевой указатель? - PullRequest
6 голосов
/ 12 августа 2010

Мне нужно написать код для функции обратного вызова (она будет вызываться из ATL, но это не очень важно):

HRESULT callback( void* myObjectVoid )
{
    if( myObjectVoid == 0 ) {
       return E_POINTER;
    }
    CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
    return myObject->CallMethod();
}

здесь void* гарантированно будет указателем на CMyClass, поэтому static_cast допустимо. Меня беспокоит то, что код должен быть максимально переносимым (по крайней мере, для более новых версий Visual C ++). Поэтому, чтобы быть супер-параноиком, я склонен также проверить указатель CMyClass* - я имею в виду, что если он окажется нулевым?

    if( myObjectVoid == 0 ) {
       return E_POINTER;
    }
    CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
    if( myObject == 0 ) {
       return E_POINTER;
    }

Является ли вторая проверка разумной? Возможно ли для static_cast превратить ненулевой указатель в нулевой указатель?

Ответы [ 4 ]

7 голосов
/ 12 августа 2010

static_cast может изменить значение указателя, если вы приведете между частями объекта с разными смещениями:

class A{ int x; }; class B{ int y; };
class C : A,B {};

C *c=new C(); 

B *b=c; 
// The B part comes after the A part in C. Pointer adjusted

C *c2=static_cast<C*>(b); 
// Pointer gets adjusted back, points to the beginning of the C part

Однако «значение нулевого указателя (4.10) преобразуется в значение нулевого указателя тип назначения. "(5.2.9-8), то есть , если c равно NULL, то b также равно NULL (и не настроено) и, таким образом, c2 установлено на NULL. Все это означает: если статическое приведение не-NULL myObjectVoid дает NULL, то значение myObjectVoid было получено путем обхода системы типов. И это означает, что компилятор может выдать второй чек, потому что "это все равно не может произойти".

6 голосов
/ 12 августа 2010

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

В конкретном случае преобразования между указателями на объекты и указателями на пустоту стандарт имеет следующее выражение (5.2.9 / 10):

Значение типа "указатель на объект", преобразованное в "указатель на void" и обратно в исходный тип указателя, будет иметь свое первоначальное значение.

и это (4.10 / 3)

Результат преобразования «указателя на T» в «указатель на void» указывает на начало места хранения, где находится объект типа T

поэтому исходные и конечные указатели объектов будут одинаковыми, а указатель void будет нулевым, если и только если указатели объектов равны.

0 голосов
/ 12 августа 2010

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

0 голосов
/ 12 августа 2010

Единственное изменение static_cast, которое следует внести в указатель, - это выравнивание слов. Итак, теоретически, из myObjectVoid, указывающего на последний байт в памяти, возможно, что он может быть «выровнен» до 0, но я не вижу в этом реальной проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...