Доступ к участникам класса на Нулевом указателе

Если у вас много данных, подумайте об индексации. Пожалуйста, ознакомьтесь с этими ссылками:

Как работает индексирование базы данных

Индексирование в поисковых системах

48
задан Niall 26 September 2014 в 01:58
поделиться

7 ответов

Объект foo является локальной переменной с типом Foo*. Та переменная, вероятно, выделяется на стеке для эти main функция, точно так же, как любая другая локальная переменная. Но значение сохраненный в foo является нулевым указателем. Это не указывает нигде. Нет никакого экземпляра типа Foo, представленного нигде.

Для вызывания виртуальной функции вызывающая сторона должна знать, какой объект к функции обращаются. Поэтому сам объект - то, что говорит, какая функция должна действительно быть вызвана. (Это часто реализуется путем давания объекту подсказки к vtable, списку указателей функции, и вызывающая сторона просто знает, что он, как предполагается, вызывает первую функцию в списке, не зная заранее, где тот указатель указывает.)

, Но назвать невиртуальную функцию, вызывающая сторона не должна знать все это. Компилятор знает точно, какая функция будет вызвана, таким образом, это сможет генерировать CALL команда машинного кода для движения непосредственно в желаемую функцию. Это просто передает указатель на объект, к функции обратились как скрытый параметр к функции. Другими словами, компилятор переводит Ваш вызов функции в это:

void Foo_say_hi(Foo* this);

Foo_say_hi(foo);

Теперь, так как реализация той функции никогда не ссылается ни на каких членов объекта, на который указывает this аргумент, Вы эффективно избегаете маркера разыменования нулевого указателя, потому что Вы никогда не разыменовываете тот.

Официально, звоня любой функция — даже невиртуальная — на нулевом указателе является неопределенным поведением. Один из позволенных результатов неопределенного поведения - то, что Ваш код, кажется, работает точно, как Вы предназначили. Вы не должны полагаться на это, хотя Вы будете иногда находить библиотеки от своего поставщика компилятора, которые делают , полагаются на это. Но поставщик компилятора имеет преимущество способности добавить дальнейшее определение тому, что иначе было бы неопределенным поведением. Не делайте этого сами.

83
ответ дан Rob Kennedy 7 November 2019 в 12:16
поделиться

say_hi() функция членства обычно реализуется компилятором как

void say_hi(Foo *this);

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

Foo не становится выделенным вообще.

17
ответ дан Niall 7 November 2019 в 12:16
поделиться

Разыменование Нулевого указателя вызывает "неопределенное поведение", Это означает, что что-либо могло произойти - Ваш код, может даже казаться, работает правильно. Вы не должны зависеть от этого однако - если Вы выполняете тот же код другой платформы (или даже возможно на той же платформе) он, вероятно, откажет.

В Вашем коде нет никакого объекта Foo, только указатель, который является initalised с ПУСТЫМ УКАЗАТЕЛЕМ значения.

7
ответ дан 7 November 2019 в 12:16
поделиться

Это - неопределенное поведение. Но большинство компиляторов сделало инструкции, которые обработают эту ситуацию правильно, если Вы не сделаете доступа к членским переменным и виртуальной таблице.

позволяют, видят, что дизассемблирование в Visual Studio для понимает то, что происходит

   Foo* foo = 0;
004114BE  mov         dword ptr [foo],0 
    foo->say_hi(); // works well
004114C5  mov         ecx,dword ptr [foo] 
004114C8  call        Foo::say_hi (411091h) 
    foo->say_virtual_hi(); // will crash the app
004114CD  mov         eax,dword ptr [foo] 
004114D0  mov         edx,dword ptr [eax] 
004114D2  mov         esi,esp 
004114D4  mov         ecx,dword ptr [foo] 
004114D7  mov         eax,dword ptr [edx] 
004114D9  call        eax  

, поскольку Вы видите Foo:say_hi, названный как обычная функция, но с это в регистре ecx. Для упрощают Вас, может предположить, что это передало как неявный параметр, который мы никогда не используем в Вашем примере.
, Но во втором случае мы вычисляющий адрес функциональной должной виртуальной таблицы - должные нечто обращаются, и получает ядро.

5
ответ дан bayda 7 November 2019 в 12:16
поделиться

a) Это работает, потому что это ничего не разыменовывает через неявное "этот" указатель. Как только Вы делаете это, бум. Я не на 100% уверен, но я думаю, что нулевой указатель разыменовывает, сделаны RW защита первого 1K пространства памяти, таким образом, существует маленький шанс nullreferencing, не будучи пойманным, если бы Вы только разыменовываете его мимо 1K строка (т.е. некоторая переменная экземпляра, которая выделить очень далеко, как:

 class A {
     char foo[2048];
     int i;
 }

тогда a-> я возможно был бы не пойман, когда A является пустым.

b) Нигде, Вы только объявили указатель, который выделяется на основном (): s стек.

2
ответ дан Pasi Savolainen 7 November 2019 в 12:16
поделиться

Вызов к say_hi статически связывается. Таким образом, компьютер на самом деле просто делает стандартный вызов к функции. Функция не использует полей, таким образом, нет никакой проблемы.

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

2
ответ дан Uri 7 November 2019 в 12:16
поделиться

В исходные дни C++ код C++ был преобразован в C. Методы объекта преобразовываются в неметоды объекта как это (в Вашем случае):

foo_say_hi(Foo* thisPtr, /* other args */) 
{
}

, Конечно, имя foo_say_hi упрощено. Для получения дополнительной информации ищите искажение имени C++.

, Как Вы видите, если thisPtr никогда не разыменовывается, то код прекрасен и успешно выполняется. В Вашем случае не использовались никакие переменные экземпляра или что-либо, что зависит от thisPtr.

Однако виртуальные функции отличаются. Существует много объектных поисков, чтобы удостовериться, что правильный объектный указатель передается как параметр функции. Это разыменует thisPtr и вызовет исключение.

1
ответ дан Tommy Hui 7 November 2019 в 12:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: