探索C++的底层机制

探索C++的底层机制

在看这篇文章之前,请你先要明白一点:那就是c++为我们所提供的各种存取控制仅仅是在编译阶段给我们的限制,也就是说是编译器确保了你在完成任务之前的正确行为,如果你的行为不正确,那么你休想构造出任何可执行程序来。但如果真正到了产生可执行代码阶段,无论是c,c++,还是pascal,大家都一样,你认为c和c++编译器产生的机器代码会有所不同吗,你认为c++产生的机器代码会有访问限制吗?那么你错了。什么const,private,统统没有(const变量或许会放入只读数据段),它不会再给你任何的限制,你可以利用一切内存修改工具或者是自己写一个程序对某一进程空间的某一变量进行修改,不管它在你的印象中是private,还是public,对于此时的你来说都一样,想怎样便怎样。另外,你也不要为c++所提供的什么晚期捆绑等机制大呼神奇,它也仅仅是在所产生的代码中多加了几条而已,它远没有你想象的那么智能,所有的工作都是编译器帮你完成,真正到了执行的时候,计算机会完全按照编译器产生的代码一丝不苟的执行。你明白我在说什么吗?对了,如果你从前接触过汇编,只要你反汇编一段c++代码,你就会说:原来是这么回事呀,c++只不过是把我们的问题进行了更高层次的抽象,但只要你解开面纱,回到问题的本源,一切都将变得不再神秘……

(以下的反汇编代码均来自visial c++ 7.0)

一.让我们从变量开始-----并非你想象的那么简单
变量是什么,变量就是一个在程序执行过程中可以改变的量。换一个角度,变量是一块内存区域的名字,它就代表这块内存区域,当我们对变量进行修改的时候,会引起内存区域中内容的改变。但是你若是学习过汇编或是计算机组成原理,那么你就会清楚对于一块内存区域来说,根本就不存在什么名字,它所仅有的标志就是他的地址,因此我们若想修改一块内存区域的内容,只有知道他的地址方能实现。看来所谓的变量一说只不过是编译器给我们进行的一种抽象,让我们不必去了解更多的细节,降低我们的思维跨度而已。例如下面这条语句:
int
a=10;
按照我们的思维习惯来讲,就是“存在一个变量a,它的值是10”,一切都显得那么的自然。我们不必去在乎什么所谓的地址以及其他的一些细节。然而在这条语句的底层实现中,a已经不能算是一个变量了,它仅仅是一个标记,代表一个地址的标记:
mov
dword
ptr[a],0Ah;
怎么样,这条语句不像上面那条易于接受吧,因为它需要了解更多的细节,你几乎不能得到编译器的任何帮助,一切思维上的跨越必须由你自己完成。这条语句应该解释为“把10写入以a为地址的内存区域”。你说什么?a有些像指针?对,的确像,但还不是,只不过他们的过程似乎是类似的。这里所说的跨越实际上就是从一个现实问题到具体地址以及内存区域的跨越。

二.引用:你可以拥有引用,但编译器仅拥有指针(地址)
看过了第一条,你一定对编译器的工作有了一定的了解,实际上编译器就是程序员与底层之间的一个转换层,它把一个高级语言代码转换为低级语言代码,一个编译器完成的转换跨度越大,那么它也就会越复杂,因为程序员的工作都由他代为完成了。C++编译器必然比汇编编译器复杂就是这个道理。如果我问你引用和指针是一样的吗?你或许会说当然不一样了,指针容易产生不安全的因素,引用却不会,真的不会吗?我们来看下面这段代码:
int
*e=new int(10);
int &f=*e;
delete
e;
f=30;
你认为上面这段代码怎么样,我感觉就不很安全,它和指针有相同的隐患。因为它所引用的内存区域就不合法。
我个人认为,所谓的引用其实就是一种指针,只不过二者的接口并不相同,引用的接口有一定的限制。指针可以一对多,而引用却只能一对一,即&refer不能被改变,但却并不能说一对一就是安全的,只不过危险的系数降低罢了。引用比指针更容易控制。
Ok,下面来说说指针,曾经有过汇编经验的人一定会说,恩,指针的某些地方有些像汇编,尤其是那个“*”,怎么就那么像汇编中的“[]”啊。呵呵,的确,它也涵盖了一个寻址的过程。看来指针的确是个比较低级的东西。然而引用却并不那么直接,虽然程序员用起来方便安全了许多。但是你要清楚,只有你可以拥有引用,编译器可没有这个工具,计算机并不认识这个东西。因此,它的底层机制实际上是和指针一样的。不要相信只有一块内存拷贝,不要认为引用可以为你节省一个指针的空间,因为这一切不会发生,编译器还是会把引用解释为指针。不管你相不相信,请看下面这段代码:
int&
b=a;
lea eax,[a];
mov dword ptr[b],eax;把a的地址赋给地址为b的一块内存

b=50;
mov eax,dword ptr[b];
mov dword ptr[eax],32h;

int *d=&a;
lea eax,[a];
mov dword ptr[d],eax

*d=60;
mov eax,dword ptr[d]
mov dword
ptr[eax],3ch;
以上的代码均来自具体的编译器,怎么样,相信了吧,好,让我再来做一个或许不怎么恰当的比拟,你一定编过有关线性表和栈的程序吧,线性表是一个非常灵活的数据结构,在他上面有许多的操作,然而栈呢,它是一个限制性操作的线性表,它的底层操作实际上是由线性表操作实现的。就好比stack与vector的关系,因此指针和引用的关系就好比线性表和栈的关系,引用也就是受限的指针,它对外的接口和指针虽然并不一样,但底层是相同的。
下面再来看看引用的一个重要用途,作为函数的参数传递的时候是怎样的情形:
void
swapr(int &a, int &b);
void swapr(int* a, int *b);

int a=10;
int b=20;

swapr(a, b);
lea eax,[a];
push eax; //把a的地址压入堆栈
lea
ecx,[b];
push ecx;
call swapr;

swapr(&a, &b);
lea eax,[a];
push eax;
lea ecx,[b];
push
ecx;
call
swapr;
怎么样,用引用和指针传递参数无论是在效率上还是在空间上都是完全一样的,如果妄想不传入地址就修改实参的值,简直就是天方夜谭,这就说明引用的本质就是指针。毕竟它们的行为都太相似了,如果不是这样,你还有什么方法去实现引用吗?记住,引用只不过是编译器为你提供的一个有用且安全的工具,对于机器代码可无法表示它,它把指针一对多的缺点去除,禁止了你的不安全的操作。但回到问题的本源,他们没有任何区别。

Ok,现在关于引用我想说的就这些,不知道你有什么看法呢,这篇文章中一定存在着许多错误以及误解,我希望你能指正,

参考

http://blog.csdn.net/delphicodehome/article/details/500847
--------------------------------------------------------------------------------
我也看过指针与引用的汇编代码,它们的底层实现是一样的。指针和引用仅仅是编译器考虑的范畴。

时间: 2024-10-29 19:06:10

探索C++的底层机制的相关文章

关联容器(底层机制) — hashtable

C++ 11已将哈希表纳入了标准之列.hashtable是hash_set.hash_map.hash_multiset.hash_multimap的底层机制,即这四种容器中都包含一个hashtable. 解决碰撞问题的办法有许多,线性探测.二次探测.开链等等.SGI STL的hashtable采用的开链方法,每个hash table中的元素用vector承载,每个元素称为桶(bucket),一个桶指向一个存储了实际元素的链表(list),链表节点(node)结构如下: template <cl

关联容器(底层机制) — 红黑树

set.map.multiset.multimap四种关联式容器的内部都是由红黑树实现的.在STL中红黑树是一个不给外界使用的独立容器.既然是容器,那么就会分配内存空间(节点),内部也会存在迭代器.关于红黑树的一些性质,可以参考"数据结构"中的笔记,这里只记录STL中的红黑树是如何实现的. 和slist一样,红黑树的节点和迭代器均采用了双层结构: 节点:__rb_tree_node继承自__rb_tree_node_base 迭代器:__rb_tree_iterator继承自__rb_

C++ STL容器底层机制

1.vector容器 vector的数据安排以及操作方式,与array非常相似.两者的唯一区别在于空间的运用的灵活性.array是静态空间,一旦配置了就不能改变.vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素.因此,vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始要求一个大块的array. vector动态增加大小,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配

探索Android该Parcel机制(上)

一.先从Serialize说起 我们都知道JAVA中的Serialize机制.译成串行化.序列化……,其作用是能将数据对象存入字节流其中,在须要时又一次生成对象.主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等. 二.Android中的新的序列化机制 在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中採用了新的IPC(进程间通信)机制,必定要求使用性能更出色的对象传输方式. 在这种环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序

Asp.net底层机制

Asp.net底层就是用户通过输入网址,然后请求IIs服务器的流程,在这个过程中有一个重要的部件就是ISAPI,这是一个底层的win32API,在扩展方面比较困难,多用于接口之间的桥接,.net和IIS通过IISAPI进行交互的,IIS中后缀名.aspx,通过应用程序扩展映射到.net的ISAPI扩展的dll,这个dll就是aspnet_isapi.dll,访问都是通过这个dll中相应的映射完成的. 1.通过asp.net的映射机制使用后缀名从ISAPI中获取到接收的请求,并将请求的路由到相应的

探索Android该Parcel机制上

一.先从Serialize说起 我们都知道JAVA中的Serialize机制.译成串行化.序列化……,其作用是能将数据对象存入字节流其中,在须要时又一次生成对象.主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等. 二.Android中的新的序列化机制 在Android系统中.定位为针对内存受限的设备,因此对性能要求更高,另外系统中採用了新的IPC(进程间通信)机制,必定要求使用性能更出色的对象传输方式.在这种环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列

(转载)JVM实现synchronized的底层机制

目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Lock孰优孰劣,只是介绍二者的实现原理. 数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令,大家可能会进一步追问:JVM底层又是如何实现synchronized的? 本文所指说的JVM是指Hotspot

windows消息钩子注册底层机制浅析

标 题: [原创]消息钩子注册浅析 作 者: RootSuLe 时 间: 2011-06-18,23:10:34 链 接: http://bbs.pediy.com/showthread.php?t=135702 windows消息钩子很古老,但目前仍有很多地方需要用到,简单跟踪了一下函数执行流程,函数用法如下(MSDN): 函数功能:该函数将一个应用程序定义的挂钩处理过程安装到挂钩链中去,您可以通过安装挂钩处理过程来对系统的某些类型事件进行监控,这些事件与某个特定的线程或系统中的所有事件相关.

foreach底层机制

简单例子 直接了解foreach底层有些困难,我们需要从更简单的例子着手.下面上一个简单例子: 1 public class Simple { 2 3 public static void main(String[] args) { 4 int i = 5; 5 System.out.println(i); 6 } 7 } 找到其字节码文件所在目录并在目录下打开终端(Windows系统是在目录下shift+鼠标右键选择在此打开powershell窗口) 输入指令:javac -Simple.cl