首先说明我下面用的编译器是VC6.0 。const 是 C/C++ 的一个关键字,尤其重要的一个区别是C只是使变量常量化,而C++会在一定程度上把它当作常量。考虑下面的代码
const int i = 2;
*(int*)&i = 0;
printf("%d\n", i ); 当文件后缀是 .c 和 .cpp 时,结果分别是 0,2 。为什么会这样,让我们看下 .cpp 时的汇编代码
00410998 mov dword ptr [ebp-4],2 8: const int a = 2;
0041099F mov dword ptr [ebp-4],0 9: *(int*)&a = 0;
004109A6 push 2 10: printf("%d\n",a );
004109A8 push offset string "%d\t%d\n" (00427004)
004109AD call printf (00410900) 从上面可以看到内存中的 a 由 2 变成了 0, 下面调用 printf 的时候, 本应该把 a 的值压入堆栈,但是编译器直接把 2 压入了堆栈,太可怕了,明明内存中 a = 0了,编译器却不闻不问,这也是C++的一个特点吧,至少你修改 a 的举动失败了。当然聪明的你可以通过给 a 赋一个运行时确定的值的办法绕过编译器的自以为是,但是这肯定违背了C++的意愿。
int i ;
scanf("%d", & i);
const a = i;
*(int*)&a = 0;
printf("%d\n" , a); 好了,不知道你有没有遇到这样的问题,如果函数的参数是结构体,函数就会傻乎乎的把整个结构体慢慢的压栈,这简直是难以想象的,所以作为一个C程序员,我总是把指针传递给函数,至于const 心情好了就添加一下,引用那是什么,从来没想过。然而,C++在这些时候总是鼓励我们使用引用,于是,我想当然得就把引用看成是一个指针(Of Course , 除了指针,还能是什么,当然你可以参考百度百科对C++引用的解释 "声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与a相等"。 这个说法是错误的,下面会告诉你 )。
然而,今天在看《一个月挑战C++》的时候,里面对引用的解释和实例却让我有了些疑问,他肯定了引用时一个特别的指针,主要是用在函数的形式参数上 ," 例如:(int &test;),这里我们就定义了一个int类型的名为test 的引用,但是int &test; 这样的方式是不能够被编译成功的,因为引用的定义必须同时给应用进行赋值操作,这里的赋值并不是说把变量的值传递给引用,而是把引用指向变量,写成这样就对了:(int &test=变量名;)" 。 下面给出的例子是
int a=10;
int &test=a; test=test+2;
你会发现&a和&test的地址显示是相同的,a和test的值显示也是一样的!这里,我并没有特别的疑惑,但是下面的例子,就有点迷茫了
int a=10;
//double &test = a + 1.2f; //这句就是错误的!
const double &test = a + 1.2f;
当然这个例子主要放在 const修饰如果用在引用上会有一个特别之处,它的奥妙就在于可以进行不同类型的对象的初始化。语法上为什么这样,就没必要深究了,但是他下面给出的解释是
道理是这样的,const修饰过后的引用在编译器内部是这样进行变化的。
int a=10;
const double &test = a + 1.2f;
这样的一段代码在编译器认为却是下面的方式进行的
int a=10;
int temp = a;
const double &test = temp + 1.2f;
这个怎么看都跟没解释一样,然后我就自己看了下汇编,我认为等价的C++代码应该更像是
register double temp = (double)a +1.2f;
const double &test = temp;
然后我又试了下下面的代码 double a = 2; double &b = a; double c = 0; 输出了下 &a , &c 的值,发现他们相差了12个字节,其中8字节被一个double类型占用,也就是说 double &b 占用了4个字节,刚刚好是一个指针的大小,而查看汇编也说明了那个地方存的就是 a 的地址(这也说明了引用是会在栈上分配内存的,大小是一个指针占用的空间)。
8: int a = 2;
00410998 mov dword ptr [ebp-4],2 ; [ebp - 4] 就是 a
9: int &b = a;
0041099F lea eax,[ebp-4] ; 直接取 a 的地址
004109A2 mov dword ptr [ebp-8],eax ; 把 a 的地址就这么赋给b了, 所以 b 是一个指向 a 的指针
10: b = 0;
004109A5 mov ecx,dword ptr [ebp-8] ;如果 b 是int 型变量,这里就应该是 mov dword ptr[ebp - 8],0 了
004109A8 mov dword ptr [ecx],0
为什么存的是指针,使用时却是当double呢!很简单,因为编译器遇到引用,就自动把 指针指向的数据转换成需要的类型。所以在参数传递中使用 引用 和 指针 在本质上是一样的,不一样的地方在于,你无法传递 NULL 指针,从而减少了对参数合理性的检查!