Сегодня состоялся следующий спор с коллегами. Они утверждали, что в таком коде нет никаких проблем, и все будет работать везде одинаково:
#include <iostream>
struct S{
int a;
void foo(){
std::cout << "hello";
}
};
int main(){
S *p = nullptr;
p->foo(); //hello
}
Мол к данным мы не обращаемся => В память по адресу 0 не лезем => Проблем нет.
Я им с пеной у рта доказывал что если вызывать любой не статический метод у nullptr
это сразу неопределенное поведение, и не важно что там в этом методе происходит.
Вопросы:
this
?В "классическом" С++ (C++98) ситуация однозначная - разыменование нулевого указателя приводит к неопределенному поведению. Соответственно вызов нестатического метода объекта через нулевой указатель приводит к неопределенному поведению. Не имеет никакого значения, выполняет ли этот метод доступ к членам класса или не выполняет. Такова позиция спецификации языка. С этой точки зрения вы совершенно правы, а аргументы ваших оппонентов на тему "все везде будет работать" - не более чем следствие "уличного образования" из разряда "смотрю в книгу ассемблер, вижу фигу".
В то же время уже довольно давно делаются попытки формирования более гибкой/тонкой спецификации в этом вопросе. В частности
DR#232: Is indirection through a null pointer undefined behavior?
Однако работа в этом направлении перманентно зависла в состоянии drafting с 2005 года. Честно говоря, создается впечатление, что какого-то внятного толкования текста нынешнего стандарта на эту тему никто дать не может, возможно именно потому, что тема до сих пор является "подвешенной".
Как вы сами понимаете, стандарт не будет заводить отдельную спецификацию на именно ваш частный случай. А как только мы переходим к более общему случаю, то сразу возникают такие ситуации, как преобразование указателя this
при вызове метода в условиях [множественного] наследования.
struct A
{
int a;
};
struct B {
int b;
void foo()
{
// К данным мы не обращаемся
// Но чему здесь равно `this`???
if (this == nullptr)
; // ???
}
};
struct C : A, B
{
};
int main()
{
C *c = nullptr;
c->foo();
}
Не ясно, должен ли компилятор при преобразованиях указателя this
в процессе вызова метода базового класса придерживаться правила "null преобразовывается в null"? Вот именно из-за таких тонкостей изначально было принято решение запретить вызовы нестатических методов через нулевой указатель. Что же будет (и есть) сейчас и каковы намерения авторов языка - надо ждать и разбираться.
Обратите внимание, кстати, что 8.5.1.2/4 требует, чтобы скрытый параметр this
при вызове метода класса инициализировался при помощи explicit type conversion. То есть в вышеприведенном примере с множественным наследованием в вызове c->foo()
указатель this
типа B
должен инициализироваться как (B *) c
. Такое преобразование работает по правилу null-в-null и результат (B *) c
тоже будет нулевым указателем. Однако в GCC и Clang this
внутри foo
во время вызова будет иметь значение 0x4
. То есть эти компиляторы не выполнили требований 8.5.1.2/4. Это сразу говорит о том, что GCC и Clang по-прежнему трактуют такой вызов как неопределенное поведение.
P.S. При этом в языке С разыменование нулевого указателя строжайше запрещено.
Код
struct S{
int a;
void foo(){
std::cout << "hello";
}
};
идентичен такому
void foo(s * this){
std::cout << "hello";
}
Таким образом вызов
S *p = nullptr;
p->foo(); //hello
эквивалентен такому
foo(nullptr)
Для проверки просто загляните в окно CPU. Оба вызова сгенерируют один и тот же код
mov eax, 0
push eax
call foo
т.е. пока Вы не обращаетесь к this
проблем нет вообще. Обращение к this
произойдет в двух случаях
А в статические функции указатель this
не передается вообще
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
есть абсолютное имя файла QString filePathНадо из программы вызвать открытие этого файла стандартной для системы утилитой
Второй день пытаюсь сделать шифрование Xor'ом, но все никак не получаетсяВот мой код
Ваши условия не покрывают случай, когда i или j равно n, а стало быть в ячейках с этими индексами остаются неинициализированные данныеПросто...
Можно ли QLineEdit сделать многострочным? Я имею ввиду чтобы там оставалась одна строка, нельзя было enter нажать, просто чтобы буквы отображались...