Почему C ++ запрещает создание допустимых указателей с допустимого адреса и типа? - PullRequest
0 голосов
/ 08 ноября 2018

Если вы знаете две части информации:

  1. Адрес памяти.
  2. Тип объекта, сохраненного в этом адресе.

Тогда у вас есть все необходимое для ссылки на этот объект:

#include <iostream>
using namespace std;

int main()
{
    int x = 1, y = 2;
    int* p = (&x) + 1;
    if ((long)&y == (long)p)
        cout << "p now contains &y\n";
    if (*p == y)
        cout << "it also dereference to y\n";
}

Однако, это не разрешено стандартом C ++. работает в нескольких компиляторах Я пытался, но это неопределенное поведение.

Вопрос: почему?

Ответы [ 4 ]

0 голосов
/ 08 ноября 2018

Это наносит ущерб оптимизации.

void f(int* x);

int g() {
    int x = 1, y = 2;
    f(&x);
    return y;
}

Если вы действительно можете «угадать» адрес y из адреса x, то вызов f может изменить y, поэтому оператор return должен перезагрузить значение y из памяти.

Теперь рассмотрим типичную функцию с большим количеством локальных переменных и большим количеством обращений к другим функциям, где вам придется сохранять значение каждой переменной в памяти перед каждым вызовом (потому что вызываемая функция может их проверять) и перезагружать их после каждого вызова. вызов (потому что вызванная функция могла изменить их).

0 голосов
/ 08 ноября 2018

Если вы знаете адрес и тип объекта, и ваша реализация ослабила безопасность указателя [basic.stc.dynamic.safety §4] , то доступ к объекту по этому адресу должен быть законным Я думаю, что через соответствующее значение.

Проблема заключается в том, что стандарт не гарантирует, что локальные переменные одного и того же типа размещаются непрерывно, а адреса увеличиваются в порядке объявления. Таким образом, вы не можете получить адрес y на основе того вычисления, которое вы делаете с адресом x. Кроме того, арифметика указателей приведет к неопределенному поведению, если вы пройдете более одного элемента за объектом ( [expr.add] ). Таким образом, хотя (&x) + 1 еще не является неопределенным поведением, сам процесс даже вычисления (&x) + 2 будет…

0 голосов
/ 08 ноября 2018

Код допустим в соответствии со стандартом C ++ (то есть должен компилироваться), но, как вы уже заметили, поведение не определено. Это связано с тем, что порядок объявления переменных не означает, что они будут расположены в памяти одинаково.

0 голосов
/ 08 ноября 2018

Если вы хотите рассматривать указатели как числовой тип, сначала вам нужно использовать std::uintptr_t, а не long. Это первое неопределенное поведение, но не то, о котором вы говорите.

Он работает в нескольких компиляторах, которые я пробовал, но это неопределенное поведение.

Вопрос: почему?

Хорошо, поэтому раздел комментариев отключился, когда я вызвал это неопределенное поведение. Это на самом деле неопределенное поведение (a.k.a. реализация определена ).

Вы пытаетесь сравнить два явно не связанных указателя:

  • &x + 1
  • &y

Указатель &x+1 - это указатель один за другим . Стандарт позволяет вам иметь такой указатель, но поведение определяется только при его использовании для сравнения с указателями на основе x. Поведение не указано , если сравнить его с чем-либо еще: [expr.eq & sect; 3,1]

Компилятор может поместить y куда угодно, в том числе в регистр. Таким образом, нет никакой гарантии, что &y и &x+1 связаны между собой.

В качестве упражнения для того, кто хочет показать, является ли это фактически неопределенным поведением или нет, возможно, начните здесь:

  • [basic.stc.dynamic.safety & sect; 3,4]

    Целочисленное значение является целочисленным представлением надежно полученного указателя, только если его тип по крайней мере такой же, как std :: intptr_t, и это одно из следующих: ...

    3.4 результат аддитивной или побитовой операции, один из операндов которой является целочисленным представлением значения указателя P, полученного безопасным способом, если этот результат, преобразованный с помощью reinterpret_cast, будет сравниваться с результатом, полученным безопасным образом указатель, вычисляемый из reinterpret_cast (P).

  • [basic.compound & sect; 3.4] :

    Примечание: Указатель за концом объекта ([expr.add]), как считается, не указывает на несвязанный объект типа объекта, который может быть расположен по этому адресу

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