65,186
社区成员




int i = 0;
int &j = i;
j = 10;
int *p = &j;
;int i = 0;
mov dword ptr [i],0 ; i所在空间赋值为0;
;int &j = i;
lea eax, [i] ; 将i的地址放入eax寄存器
mov dword ptr[j],eax ; 将i的地址放在赋值给j. 这很费解。 疑点1
; j = 10;
mov eax, dword ptr[j] ; 取j的地址放入eax, j的地址跟i的地址相同
mov dword ptr [eax], 0Ah ; 将j指向的地址修改为10;
;int *p = &j;
mov eax,dword ptr [j] ; 疑点2,
mov dword ptr [p],eax
class CTest
{
public:
CTest(int &nVal)
: m_nRefVal(nVal)
, m_nVal(nVal * 1000)
{
}
public:
int m_nVal;
int &m_nRefVal;
};
int _tmain(int argc, _TCHAR* argv[])
{
int i = 10;
CTest objT(i);
objT.m_nRefVal = 1000;
system("pause");
return 0;
}
objT的大小为8,根据其声明顺序,objt的内存布局是 m_nVal -> m_nRefVal;
其中m_nVal = 2710; m_nRefVal地址存放的是i的地址,我的例子中i的地址是0x002dfef8
所以objT的内存布局是, 10, 27, 00, 00, f8, fe, 2d, 00
再执行objT.m_nRefVal = 1000时, 执行如下代码
mov eax,dword ptr [ebp-14h] // 取objT.m_nRefVal的值,即i的地址放入eax寄存器
mov dword ptr [eax],3E8h // eax的值指向的地址赋值为0x3e8;
这不就是典型的指针么?
int i = 0;
int* j = i;
*j = 10;
int* p = j;
我们再看看汇编:
;int i = 0;
mov dword ptr [i],0 ; i所在空间赋值为0;
;int* j = i;
lea eax, [i] ; 将i的地址放入eax寄存器,此时eax保存着i的地址
mov dword ptr[j],eax ; 将i的地址存入j.如果把j看做指针,那j里面存的不本来就应该i的地址吗,有何疑点?
;* j = 10;
mov eax, dword ptr[j] ; 此处你原话有误,这句的意思是把j的值,即i的地址存入eax
mov dword ptr [eax], 0Ah ; 把i所在的数据,即j指向的地址的值改为10.
;int *p = j;
mov eax,dword ptr [j] ; 这里我也没看出有什么疑点,此句的意思是把j的值,即i的地址存入eax
mov dword ptr [p],eax 这里就是把j的值,即i的地址存入p所在的位置了,有何不对?
#inlcude<cstdio>
int main(){
struct ref_int
{
ref_int(int &var):r(var){};
int &r;
};
int a;
ref_int ref(a);
printf("&a =%p &ref.r =%p &ref = %p, ",&a,&ref.r,&ref );
// 结果如下
// &a == & ref.r ,这是对引用取地址的结果,根据引用即别名,引用的地址就是被引用对象的地址。所以二者地址相同
// &ref 就是储存引用这种数据的地址,和&a 是不同的
}
int i=1;
int* p=&i;
*p=1;
int i=1;
int& p=i;
p=1;
这两段代码的区别,在于第一段代码中,编译器并不了解我们对i取地址"&"的行为
实际上只是希望之后可以对i赋值,而并不关心取地址本身这个操作,由于它无法
了解我们的目的所在(这段代码中可能能了解,但再复杂一点就不行了),因此编译器
将不得不让变量i持有一个栈上的内存空间,以便使i确实拥有一个地址可以被"&",
而在第二段代码中,由于我们使用了引用,编译器将了解我们只是想通过某种方式
访问到i这个变量,而完全不关心i的地址,因此编译器可以根本不给变量i申请内存地址,
而是让i从头到尾都存放在某个寄存器中,使得对i的读写实际上都变成对寄存器的读写.
而根本不需要和内存交互.
也就是说,任何对取地址的操作,都会使编译器产生一种:"这个被取地址的变量一定要在内存里"的错觉.
例如,有时我们需要强制类型转换,经常遇见这种代码:
int32 i=0x313671;
//.................
float k=*(float*)&i;
我们的目的无非就是希望i中的每一个bit都被完整地复制到k中,而编译器不这样认为,编译器认为
你对i进行了&操作,那么i必然在内存中,这段代码就会产生内存读写操作.
C++中上例的正确方法应该是:
int32 i=0x313671;
union {
int a;
float b;
} conv;
conv.a=i;
float k=conv.b;
没有取地址操作,则编译器能对这段代码更好地优化.将i和k全部放入寄存器中.
同理的,C语言中经常有函数是如下形式:
typedef struct Data_tag {
.........
.........
.........
} Data;
void process(Data* y,Data* x){...};
.............
Data a,b;
process(&a,&b);
其中无论编译器决策process函数是否inline,C语言(C++同理)都会认为无论如何,对变量
a和b必须进行取地址的操作,在process函数中代表a和b的必须是指针而不是其他形式.
而这样:
struct Data {
.........
.........
.........
};
void process(Data& y,Data& x){...};
.............
Data a,b;
process(a,b);
时,如果编译器认为process函数不能inline,那么这两段代码的行为是一致的,但一旦遇到
可以inline process函数的情况,编译器会马上了解到我们在process函数中只是需要处理
a和b,而不在乎a和b的地址.编译器不需要创建一个指针来指向a和b的地址,而是直接在process函数
中使用a和b所在的地址就可以了.
所以在一些对性能要求严格的位置,到底是使用引用还是使用指针,是很有讲究的.
对于编译器来说,使用引用可能能够带来更多的优化空间(虽然自由度下降了).