C++的底层机制

2016-02-19 17:41 6 1 收藏

有一种朋友不在生活里,却在生命力;有一种陪伴不在身边,却在心间。图老师即在大家的生活中又在身边。这么贴心的服务你感受到了吗?话不多说下面就和大家分享C++的底层机制吧。

【 tulaoshi.com - 编程语言 】

c++为我们所提供的各种存取控制仅仅是在编译阶段给我们的限制,也就是说是编译器确保了你在完成任务之前的正确行为,假如你的行为不正确,那么你休想构造出任何可执行程序来。但假如真正到了产生可执行代码阶段,无论是c,c++,还是pascal,大家都一样,你认为c和c++编译器产生的机器代码会有所不同吗,你认为c++产生的机器代码会有访问限制吗?那么你错了。什么const,private,统统没有(const变量或许会放入只读数据段),它不会再给你任何的限制,你可以利用一切内存修改工具或者是自己写一个程序对某一进程空间的某一变量进行修改,不管它在你的印象中是private,还是public,对于此时的你来说都一样,想怎样便怎样。另外,你也不要为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;
   怎么样,用引用和指针传递参数无论是在效率上还是在空间上都是完全一样的,假如妄想不传入地址就修改实参的值,简直就是天方夜谭,这就说明引用的本质就是指针。究竟它们的行为都太相似了,假如不是这样,你还有什么方法去实现引用吗?记住,引用只不过是编译器为你提供的一个有用且安全的工具,对于机器代码可无法表示它,它把指针一对多的缺点去除,禁止了你的不安全的操作。但回到问题的本源,他们没有任何区别。

来源:http://www.tulaoshi.com/n/20160219/1615840.html

延伸阅读
在学习c/c+过程中,指针是一个比较让人头痛的问题,稍微不注重将会是程序编译无法通过,甚至造成死机。在程序设计过程中,指针也往往是产生隐含bug的原因。下面就来谈谈指针的应用以及需要注重的一些问题,里面也许就有你平时没有注重到的问题,希望能帮助各位读者理解好指针。 !-- frame contents -- !-- /frame contents -- ...
在C++中,以类、虚函数等为代表的数据抽象功能一直是C++的核心和难点。这里我想结合自己的使用经验,谈谈对C++中抽象的一点浅薄看法! 我认为C++的抽象应该是指:从我们需要解决的问题出发,在与该问题相关的一组关联对象中提取出主要的或共有的部分――说简单一点,就是用相同的行为来操作不同的对象。 从提出问题到找出与该问题...
<C++实践系列C++中的异常(exception) 作者:张笑猛 提交者:eastvc 发布日期:2003-11-22 14:40:53 原文出处:http://objects.nease.net/ 1.简介   1.1常用的错误处理方式   1.2 不常用的处理方式   1.3 异常 2. 异常的语法   2.1 try   2.2 catch   2.3 throw   2.4 函数声明 3. 异常使用技巧...
<C++实践系列C++中的引用(reference) 作者:张笑猛 提交者:eastvc 发布日期:2003-11-22 14:44:07 原文出处:http://objects.nease.net/ 1.简介 2.引用的语法 3.引用使用技巧     3.1 引用和多态     3.2 作为参数     3.3 作为返回值     3.4 什么时候使用引用 4....
   持久对象 (persistent objects)广泛应用于游戏、分布式数据库系统、多媒体以及图形应用程序中。目前C++并不直接支持持久性(persistence)(但有一些在C++未来版本中添加持久性和反射(reflection)的建议)。 !-- frame contents -- !-- /frame contents -- 持久对象可以在创建它的程序的作用域之外保持自身状态。...

经验教程

580

收藏

80
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部