Re[6]: ((SA*)0)->Func();
От: VVV Россия  
Дата: 30.04.02 11:40
Оценка:
Здравствуйте Андрей Тарасевич, Вы писали:

АТ>Здравствуйте Павел Кузнецов, Вы писали:


АТ>>>Может написано, может не написано. Это, в принципе, неважно. Стандарт вместо этого совершенно глухо запрещает все возможные пути падания внутрь метода объекта через null-указатель. А именно: запрещается разадресовывать null-указатель. Это автоматически означает, что, пока поведение программы определено, внутри метода объекта this никак не может быть равен null-указателю.


ПК>>AFAIK, вызов метода сам по себе не подразумевает разыменования указателя. Поэтому в стандарте (9.3.1/1) явно указано, что "If a nonstatic member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined". Т.к. null-pointer гарантированно не является указателем ни на какой объект, это предложение включает и вызов метода через null-pointer.


АТ>Я не согласен. Возможность вызова метода класса (причем как статического, так и нестатического) через null-указатель перекрыта стандартом еще раньше. Стандарт языка подчеркивает, что выражение, стоящее слева от '.' или '->' при доступе к члену класса (методу или полю), обязательно вычисляется. Причем оно вычисляется независимо от того, является ли доступаемый член класса статическим или нет. Вычисление этого выражения автоматически означает разыменование использованного в нем указателя. Разыменование null-указателя порождает неопределенное поведение.


АТ>Из приведенной тобой выдержки, кстати, может показаться, что к статическим членам класса можно доступаться через null-указатель. Это не так. Любое вычисляемое выражение, содержащее разыменование null-указателя порождает неопределенное поведение. А вот например параметр 'typeid' может содержать разыменованный null-указатель, т.к. выражение-параметр 'typeid' не вычисляется.


АТ>Также можно заметить, что если бы вызов метода не подразумевал разыменования указателя, то стандарт языка С++ должен был бы содержать отдельное замечание по поводу вызова виртуальных методов. Эти последние вызывать через null-указатель все таки нельзя (мы все в глубине души знаем, что такой вызов существенно подразумевает разыменование указателя). Я не нашел в стандарте такого замечания.


Вычисление адреса(указателя) совсем не то же самое, что и доступ(разыменование) по этому адресу. Поэтому в языке есть специальные операторы -> и * для разыменования указателей, компилятор не обращается к данным без прямого указания программиста. Что значит у Вас выражение "Вычисление этого выражения автоматически означает разыменование использованного в нем указателя"??? Разыменование — это есть способ получение данных по адресу, в Вашем выражении сколько данных будет "автоматически" получать компилятор?? 1 байт? 2 байта? или... сколько и куда???

Не надо забывать, что языки, такие как C и C++ используются для написания программ не только на PC но и для различных устройств(типа мобильных телефонов), а там организация памяти может быть абсолютно любой (т.е. по NULL адресу могут находиться системные структуры). К тому же, по Вашему заявлению, получается, что корректно заполнить таблицу прерываний с помощью C++ нельзя, ибо начинается эта таблица по адресу 0? Т.е. специально для этого надо делать ассемблерные вставки? Да, NULL (он же 0) — особое число и его можно сравнивать с указателями без приведения типа и можно сигнализировать об ошибках выделения памяти(на PC), но не более того.

пример корректного применения NULL указателя:
#include "stdafx.h"

class Mem{
public:
    unsigned char m[0x400000];
    unsigned char prg[256];
    void Print()
    {
        for(int i=0; i < 256; i++)
            printf("%c", prg[i] >= (unsigned char)' ' ? prg[i] : '.');
        printf("\n");
    }
};


int main(int argc, char* argv[])
{
    Mem *pm=(Mem*)NULL;
    
    for(int i=0; i < 256; i++)
        printf("%c", pm->prg[i] >= (unsigned char)' ' ? pm->prg[i] : '.');
    printf("\n");
    
    pm->Print();

    return 0;
}


если бы Вы привели конкретное место в стандарте (хотя бы ссылку) может быть этой дискуссии и не было бы :)
стандарт о ((SA*)0)->Func();
От: Павел Кузнецов  
Дата: 30.04.02 12:16
Оценка:
#Имя: FAQ.cpp.*nullptr.std
Здравствуйте VVV, Вы писали:

DG>Должен ли по стандарту работать следующий код?


struct SA
{
  void Func() //невиртуальная
  {
    if (this == NULL) 
      return;
  }  
  static void StaticFunc() {}
};

void main()
{
  ((SA*)0)->StaticFunc(); //ИМХО, тут должно быть все нормально
  ((SA*)0)->Func(); //а вот здесь не понятно, мало ли чего может компилятор напихать
}

VVV>если бы Вы привели конкретное место в стандарте (хотя бы ссылку) может быть этой дискуссии и не было бы

Андрей прав. Закрываем дискуссию, вот соответствующие выдержки из стандарта:

5.2.5 Class member access (expr.ref)
1 A postfix expression followed by a dot . or an arrow ­->, optionally followed by the keyword template (14.8.1), and then followed by an idexpression, is a postfix expression. The postfix expression before the dot or arrow is evaluated; 58) (...)
58) This evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the idexpression denotes a static member.
3 If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2; (...)

1.9 Program execution (intro.execution)
4 Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer).

8.3.2 References (dcl.ref)
4 (...) [Note: in particular, a null reference cannot exist in a welldefined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior. (...)
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 30.04.02 15:56
Оценка:
Здравствуйте VVV, Вы писали:

VVV>Вычисление адреса(указателя) совсем не то же самое, что и доступ(разыменование) по этому адресу. Поэтому в языке есть специальные операторы -> и * для разыменования указателей, компилятор не обращается к данным без прямого указания программиста. Что значит у Вас выражение "Вычисление этого выражения автоматически означает разыменование использованного в нем указателя"??? Разыменование — это есть способ получение данных по адресу, в Вашем выражении сколько данных будет "автоматически" получать компилятор?? 1 байт? 2 байта? или... сколько и куда???


Нет. Разыменованием в С++ называется применение к указателю операторов '*' или '->'. Получаются ли кем-то указуемые данные или не получаются — неважно. Если есть применение '*' и '->' в вычисляемом выражении — згачит есть разыменование.

VVV>Не надо забывать, что языки, такие как C и C++ используются для написания программ не только на PC но и для различных устройств(типа мобильных телефонов), а там организация памяти может быть абсолютно любой (т.е. по NULL адресу могут находиться системные структуры).


Конкретные компиляторы выбирают значение null-указателя таким, чтобы он не указывал ни ни какие данные, к которым может понадобится доступ из программы. Если на некотором устройстве по адресу 0x0000 сидят какие-то данные, с которыми нужно уметь работать из С/С++ программ, то в компиляторе С/С++ для этого устройства значение null-указателя будет не 0x0000, а каким-то другим, например, 0xFFFF.

Не забывай одной важного факта: то, что null-указатель получается путем преобразования целочисленной константы 0 к типу "указатель", совсем не означает, что битовое представление null-указателя стостоит из одних нулей. Т.е. это совсем не означает, что null-указатель — это указатель на адрес 0x0000. Null-указатель может представляться абсолютно любым физическим адресом. Каким — зависит от платформы.

VVV>К тому же, по Вашему заявлению, получается, что корректно заполнить таблицу прерываний с помощью C++ нельзя, ибо начинается эта таблица по адресу 0? Т.е. специально для этого надо делать ассемблерные вставки? Да, NULL (он же 0) — особое число и его можно сравнивать с указателями без приведения типа и можно сигнализировать об ошибках выделения памяти(на PC), но не более того.


Также хочу напомнить, что речь здесь идет о языках С/С++, как они описаны в своей спецификации. Разнообразные платформенно-зависимые расширения никаким боком к этой дискусии не относятся.

Если на конкретной платформе разыменование null-указателя не приводит к проблемам или даже используется для выполнения каких-то полезных дейсивий — флаг ей в руки. Но к корректному С/С++ это никаким боком относится не будет. Точнее, это вообще никаким боком к С/С++ относится не будет.

VVV>пример корректного применения NULL указателя:


С точки зрения С/С++ программа прождает неопределенное поведение, т.к. содержит разыменование null-указателя. Да и еще масса проблем (с точки зрения С/С++) в этой программе присутствует.

VVV>если бы Вы привели конкретное место в стандарте (хотя бы ссылку) может быть этой дискуссии и не было бы


Хе, хе... Что-то мне подсказывает, что приведение выдержек из стандарта дискусси не остановит. Anyway, см. письмо Павла рядом.
Best regards,
Андрей Тарасевич
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.