Почему следующий код печатает 0? т.е. почему переменная a
расположена сразу после переменной d
, даже если между ними объявляется переменная c
указателя?
#include<iostream>
using namespace std;
int main () {
unsigned int a = 100;
unsigned int &b = a;
unsigned int *c = &b;
unsigned int d = (unsigned int)(c);
int e = &d - c;
int &f = e;
e ++;
cout << e << " " << endl;
return 0;
}
Работа в обратном направлении:
e ++;
cout << e << " " << endl;
Если это печатает 0, то значение e
перед выполнением этого кода должно быть -1
.
int e = &d - c;
Таким образом, результат вышеупомянутого вычитания адреса должен быть -1
.
unsigned int a /* = whatever, the value of a doesn't matter */;
unsigned int &b = a;
unsigned int *c = &b;
unsigned int d /* = whatever, the value of d doesn't matter */;
b
является ссылкой на a
, поэтому &b
эквивалентно &a
.
Итак, &d - c
эквивалентно &d - &a
, и это вычитание дает -1
.
Вывод: адрес d
является sizeof (unsigned int)
после адреса a
. (Вычитание указателя масштабируется по размеру указанного типа.)
Вероятно.
На самом деле поведение вычитания указателей на два независимо определенных объекта не определено. Стандарт говорит буквально ничего о том, что он должен делать.
На практике компилятор, вероятно, сгенерирует простейший возможный код для вычитания указателя, и этот простой код, вероятно, будет рассматривать несвязанные указатели, как если бы они были сопоставимы, хотя язык не говорит, что они есть.
Вероятно, с учетом выхода вашей программы, чтобы b
и d
были распределены рядом друг с другом. Ничто не говорит о том, что объявленные переменные должны быть распределены в том порядке, в котором вы их объявляете. Если вы хотите, чтобы объекты были выделены в памяти в порядке определения, поместите их в struct
или сделайте их элементами массива.
Вероятно также, что одна и та же программа даст разные результаты, если вы запустите ее в другой системе или в той же системе с другим компилятором или в той же системе с тем же компилятором с различными параметрами компилятора. В принципе, он может даже вести себя по-разному со всем тем же, но на другой фазе Луны.
И компилятору разрешено предположить, что поведение вашего кода хорошо определено и выполняет преобразования, которые являются действительными только с учетом этого предположения. Фактически, вычитая два несвязанных указателя, вы пообещали компилятору, что оба они указывают на элементы одного и того же объекта массива или только за ним (где один объект рассматривается как 1-элементный массив) (или что оба являются нулевыми указателями, это одно отличие между C и C++). Вы лгали компилятору, а это значит, что он не несет никаких дополнительных обязательств перед вами.
Не делай этого.
Если вы явно не размещаете объекты с помощью собственной системы управления памятью, их относительные позиции в памяти будут compiler- и зависят от системы.
ваша строка int e = &d - c;
вычитает 2 unsigned int *
.
В памяти &d
- 8 байт дальше, а затем c
(это зависит от вашей системы, но мы полагаем, что int
составляет 4 байта). Эффективно вы создаете свой стек таким образом:
unsigned int a = 100; // &a is 0x0
unsigned int &b = a; // &b is 0x0 (it just an alias)
unsigned int *c = &b; // &c is 0x4
unsigned int d = (unsigned int)(c); // &d is 0x8
unsigned int
использует 4 байта в памяти. Итак, когда вы делаете &d - c
, он должен возвращать 2
, потому что вы используете арифметику указателя с unsigned int*
(4 * 2 = 8);
Вы можете попробовать с int e = (short*)&d - (short*)c
результат должен быть 4
потому что short
размер равен 2 (2 * 4 = 8).
Вы можете попробовать с int e = (char*)&d - (char*)c
результат должен быть 8
потому что размер char
равен 1 (1 * 8 = 8).
Попробуйте распечатать переменные и адреса, чтобы понять:
#include<iostream>
using namespace std;
int main () {
unsigned int a = 100;
unsigned int &b = a;
unsigned int *c = &b;
unsigned int d = (unsigned int)(c);
int e = (short*)&d - (short*)c;
//int &f = e;
//e ++;
cout << "&a: " << (unsigned int)&a << endl;
cout << "&b: " << (unsigned int)&b << endl;
cout << "&c: " << (unsigned int)&c << endl;
cout << "&d: " << (unsigned int)&d << endl;
cout << endl;
cout << " a: " << a << endl;
cout << " b: " << b << endl;
cout << " c: " << (unsigned int)c << endl;
cout << " d: " << d << endl;
cout << endl;
cout << " e: " << e << endl;
return 0;
}
Здесь, с int e = (short*)&d - (short*)c;
, результат:
&a: 3220197356
&b: 3220197356
&c: 3220197360
&d: 3220197364
a: 100
b: 100
c: 3220197356
d: 3220197356
e: 4
b
самом делеb
- это ссылка, привязанная кa
, поэтому процесс «обратной работы» должен технически включать один дополнительный шаг:&b
на самом деле&a
, что означает, что&d - c
эквивалентно&d - &a
. Но ФП, видимо, это уже знает, поскольку это именно то, что указано в вопросе.