从C语言观点谈谈内存寻址

最近看了一些书籍,由此想把以前所学的关于对内存的思考的一些知识点整理下。

计算机的寻地址分为指令寻地址和操作数寻址,由于这些和组成原理还有汇编语言联系的很紧,但我这里要说的是仅仅从C语言的角度去谈谈寻址问题。

首先的操作数寻址的问题,关于这个其实C语言提供了很好很强大的一个玩意,它的名字叫做指针,正是由于它,我们才说C语言是比较接近底层的语言,因为通过它我们可以剖析计算机内存的奥秘。比如说一个超级easy的程序:

int main()

{

int a = 9;

printf(%d/n,a);

return 0;

}

很简单吧,首先我们定义了一个整形变量,然后赋值打印输出。我们在VC6.0下跑一下可以看到a的地址,它是0x0012ff7c。其实说句实话这个地址我们看到很多,就我的观点看这个0x0012ff7c是编译器每次给用户分配的一个数据段的首地址,也可以说堆栈段吧,你定义一个charfloat的都是分配的这个地址,如果是double的话则变成了0x0012ff8,为什么变了应该很简单吧(自己去想)。说了这么多还没说到寻址问题呢,就我看呢,其实用汇编语言来谈这个更好说明点,不过本人才疏学浅汇编没学到家所以就用C来说一下,以后等汇编学完了就用汇编来说明这个问题。其实我们程序写完以后,各种各样的变量编译器会根据其类型放在不同的数据段中,具体的有哪些类型了。总的来说用两类局部变量和外部变量。而外部变量又会分为初始化的和未初始化的两类。我们可以通过一段程序来看一下。

#include <stdio.h>

char global_c1;

char global_c2 = 'g';

 

int gloabal_i1;

int gloabal_i1 = 9;

 

char global_c3;

 

int main()

{

    char local_c1;

    char local_c2 = 'i';

 

    int local_i1;

    int local_i2 = 1;

 

    char local_c3;

    return 0;

}

VC6.0下忽略警告,调试,在watch窗口输入每个变量的地址会很清晰的看到编译怎么在为变量分配内存,然后根据地址画出一个内存分配图。这里我就不画了,结果是会有3个段,其中局部变量的段首地址为0x0012ff7c,全局变量有两段,初始化和未初始化的。

    其实这些东西只有理解了指针都是很好理解的,最近还发现了一个原来误解的东西,就是谭浩强的书上说不能把一个具体的数值赋给指针变量,我想老谭的意思应该是这样,不能随便的把一个数值简单的赋值给变量,比如你这样写显然是不能的:

int main()

{

    int *p;

    p = 12;

    *p = 4;      

    return 0;

}

这样你编译一下,编译没问题,运行就有内存错误了,原因在于你这个12的地址可能是操作系统保护起来的地址空间不能随便的读写的,你如果这样写就能行了

#include <stdio.h>

int main()

{

    char *p;

    p = (char*)0x0012ff78;

    *p = 'a';    

    printf("%c/n",*p);

    return 0;

}

这个就能运行正常了,原因是在VC6.00x12ff7c会作为局部变量数据段的起始地址来分配,0x0012ff78是下一个变量的地址,所以这段内存是安全的,也没有产生重叠,否则你如果给个0x0012ff79问题是很麻烦分析的。

然后在看看指令寻地址,这个是我们的伟大的计算机之父冯·诺依曼关于计算机的工作原理中叙述过。简单来说就是8个字:“程序存储,顺序执行”,代码和数据一样也是以二进制的形式放在内存中,在执行程序的过程中,CPU通过一个叫做程序计数器的玩意来控制程序的流程,简单来说就是PCIntel80x86可以用IP这个寄存器来等价,每执行完一条指令,PC的值自动增加,既PC总是指向下一个将要被执行的指令的地址。但顺序执行并不意味着按顺序一成不变的执行下去,我们的高级语言中都有循环,选择来控制程序的流程,这是同样通过改变PC来实现的,只不过PC的值改变的剧烈一些而已。总而言之,指令寻址相对于操作数寻址要简单的多,当一条指令执行完后,如果指令中不含有剧烈改动程序计数器的操作,那么程序计数器会自动增加,于是便形成了下一条指令的地址。

下面我们用C语言程序为例子看看如何在执行的。

int a = 3;

if (a == 2)

{

    a = 3;

}

else

{

    a = 2;

}

我们可以调试看看汇编代码:

 int a = 3;

00401028   mov         dword ptr [ebp-4],3

5:        if (a == 2)

0040102F   cmp         dword ptr [ebp-4],2

00401033   jne         main+2Eh (0040103e)

6:        {

7:            a = 3;

00401035   mov         dword ptr [ebp-4],3

8:        }

9:        else

0040103C   jmp         main+35h (00401045)

10:       {

11:           a = 2;

0040103E   mov         dword ptr [ebp-4],2

12:       }

13:   }

CPU首先使用CMP来比较a0是否相等,然后发生跳转。关于更详细现在本人才疏学浅暂时不能分析的很透彻,以后等汇编语言学好了在来谈谈寻址问题,就写到这儿吧。

 

注明:文章中参看了左飞的《代码揭秘》一书,有部分内容来自此书,写出来只是为了记录下自己学习的情况,欢迎指正。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值