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,
Андрей Тарасевич
http://files.rsdn.org/2174/val.gif
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.