C 的底层机制

2008-02-23 05:41:26来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

  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;

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

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: gcc对C语言的扩展:标签变量(Labels as Values)

下一篇: 如何在C语言中巧用正则表达式