C语言——指针篇(一)指针变量和普通变量的区别
前言:虽然经常使用C语言进行开发,但对于其指针的理解总还是有些模糊。在这里专门写篇博客记录下一些常见的指针问题。
指针变量与普通变量的区别:
首先,我们要树立一个概念–C中所有的变量名其实相当于一个地址的别名。这部分将地址和变量名联系起来的工作由编译器替我们完成,它在编译时候会生成一个对应的符号表。下面举一个例子来阐述二者之间的联系和区别。 假设我们现在编译运行代码的机器是32位机,那么变量的逻辑可寻址空间范围为0x0000_0000~0xFFFF_FFFF。存储字节序采用小端存储(即数据的高字节保存在低地址中)。 有如下代码:
char a; int b;
分析可得变量a实际占1个字节存储大小,变量b此时占4个字节存储大小。再假设编译器给它们分配空间的起始地址是从0x5000_0000处开始。如下图所示,在编译成机器指令时,编译器会把程序运行时所有用到a的地方用地址0x5000_0000代替,并且a的大小为一个字节,所以把0x5000_0000装载到MAR(主存地址寄存器),就会从内存中取出一个字节的数据送到MDR(主存数据寄存器)中。此时,a的内容才为0x0F;同理可得,当程序需要用到变量b进行计算时,首先将b对应的内存地址是0x5000_0001送到MAR,因为b的大小为4个字节,所以就从该地址中取出4个连续的字节内容送到MDR中,以供程序运算,此时b的内容是0x0102_0304。 当有如下代码时,情况发生了变化:
char *a; int *b;
如下图所示,我们假设编译器给变量名分配地址时,变量a的关联地址是0x5000_000,变量b的关联地址是0x5000_0004(它们具体的对应地址可能会因为每次编译不同而对应不同)。这里值得注意的是,笔者曾经在以前的博客提到,指针变量的所指向的存储空间与它指向哪一种数据类型无关,而与当前的机器字长有关。在32位机中,指针变量的大小是4个字节。当程序运行需要变量a时,它开始执行以下几步: 1.它将a对应的0x5000_0000载入到MAR 2.指针类型在32位机中,占4个字节,所以它会从当前地址取出连续的4个字节送到MDR 3.由于编译器在编译阶段就会发现a是指针类型,所以她的指令应该是间接寻址方式,这时会把地址数据从MDR传送到MAR 4.这时才会用到a指向的是char类型这一点,从当前地址中取出一个字节,为0x0F 同理可以类推,只有在第二次把地址载入MAR时,取数才会与具体的指向的数据类型相关,第一次是指针类型时都为4个字节。
总结:指针和普通变量的区别在于 1)寻址方式不同: 普通变量采用直接寻址方式,只需要一次就可以从内存中取出需要的数据。而指针变量是间接寻址方式,且至少进行两次。值得注意的是,理论上,一维指针变量寻址两次,二维指针变量寻址三次…n维指针变量寻址n+1次,且n可以无限增加,但是实际上,不同的寻址方式比如两次寻址和三次寻址,是在设计之初就被编进指令集中的,指令码的限制使得大多数寻址控制在两到三次,也就是我们说的一维指针和二维指针,与其可类比的概念的有一维数组和二维数组,但它们又不完全相同,后续会接着说到。 2)存储空间不同: 在上述中,普通变量总共占用的空间为5个字节,而指针变量因为采用指针指向内存地址取值的方式,额外增加了两个指针地址的存储空间为13个字节。 3)操作方式不同 : 对于普通变量,只允许修改它的内容;对于指针,可以修改指针变量的指向地址和该地址的内容。