《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

目录

指针和数组

1. 指针

1.1 如何看待下面代码中的a变量?

1.2. 什么是指针?

2.1. 指针 和 指针变量又有何不同?我们口语中的"定义一个指针"究竟是什么意思?我们该如何理解这种说法?

2.2.1. 代码理解

2.2.2. 代码理解*p进行解引用的时候,p对应的左值还是右值?

3. 为什么要有指针

3.1. 回答一个问题:为何每间宿舍都要有门牌号呢?

3.2. 那又有一个问题:CPU如何拿取数据?

4. 究竟该如何理解编址

5.1. 指针的内存布局

5.2. 指针的解引用

5.3. int *p = NULL 和*p=NULL的区别

5.4. 如何将数值存储到指定的内存地址 

5.5. 编译器的bug


本章节文章是作者通过观看《C语言深度剖析》等各种资料总结的精华,基础部分省略了不少,是为了让大家能够更加深入了解C语言的魅力!因为为了避免与之前的文章发生赘述,所以就直接讲作者认为的精华部分哈!现在正文开始!  

 谁都不能阻挡你成为更优秀的人。    

指针和数组

1. 指针

指针是什么 在回答这个问题之前,我想先问:

1.1 如何看待下面代码中的a变量?

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

反汇编可以看一下: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)  

重新理解变量:

定义一个变量,本质是在内存中根据类型来进行开辟空间。有了空间,就必须具有地址来标识空间,来方便 CPU 进行寻 址。有了空间,就可以把数据保存起来。所以,目前我们先讨论变量的空间和内容这两个概念

1.2. 什么是指针?

指针就是地址!那么地址本质是什么呢?地址是数据,那么数据可不可以被保存在变量空间里面呢?当然可以。
1.3. 有没有指针变量这个概念?
保存指针 ( 地址 ) 数据的变量就叫做指针变量

2.1. 指针 和 指针变量又有何不同?我们口语中的"定义一个指针"究竟是什么意思?我们该如何理解这种说法?

严格意义上,指针和指针变量是不同的,指针就是地址值,而指针变量是 C 中的变量,要在特定区域开辟空间,要用来保存地址数据,还可以被取地址。( 先分开 ) 但是,我们经常在口语化表达的时候,又经常将这两个概念混合,具体原因无从考证,不过个人认为与最早的 C 资料 ( 书,文档之类) 的翻译有关。然后,书与书之间互相借鉴,形成了这样的说法。同时,简化说法,也更符合人的表达习惯,估计老外也是这么想的。( 在关联 ) 那么我们以后怎么认为呢?我们分开理解,但是依旧关联使用。自己使用的时候,混合使用可以。和别人讨论,最好明确概念。

2.2.1. 代码理解

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

所以说我们平时使用的指针,严格上讲是指针变量(因为有左值右值)。

个人认为最好区分指针和指针变量的方法就是:

有没有用他的空间,或是说他有没有开辟空间,有就是指针变量。

2.2.2. 代码理解*p进行解引用的时候,p对应的左值还是右值?

int a=10;

int *p=&a;

*p=10;

int b=*p;

左边是*p取的是a的空间(左值),是把10放了进去。

右边的*p取的是a的值(右值),是把a放进了b的空间。

对指针解引用,代表的是指针所指向的目标。

结论:指针就是地址,指针变量是一个变量,变量内部保存指针(地址)数据。

3. 为什么要有指针

3.1. 回答一个问题:为何每间宿舍都要有门牌号呢?

如果要一个外人来宿舍找你,是不是有门牌号更好找? 结论:提高查找效率。
类比到计算机中
CPU在内存中寻址的基本单位是多大? 在32位机器下,最多能够识别多大的物理内存? 既然CPU寻址按照字节寻址,但是内存又很大,所以,内存可以看做众多字节的集合

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

其中,每个内存字节空间,相当于一个学生宿舍,字节空间里面能放8个比特位,就好比同学们住的八人间,每个人是一个比特位。 每间宿舍都有门牌号就等价于每个字节空间对应的地址,即该空间对应的指针。 那么,为何要存在指针呢? 为了CPU寻址的效率。如果没有,该怎么找在字节空间中的数据呢?

3.2. 那又有一个问题:CPU如何拿取数据?

CPU首先要先知道拿取数据的地址,这个地址是内存通过地址总线传输给CPU的,然后CPU拿着这个地址又通过数据总线去内存读取数据。

4. 究竟该如何理解编址

首先,必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据传递。 但是硬件与硬件之间是互相独立的,那么如何通信呢?答案很简单,用"线"连起来。 而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。 不过,我们今天关心一组线,叫做地址总线。 CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址(就如同宿舍很多,需要给宿舍编号一样) 计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。 钢琴 吉他 上面没有写上“都瑞咪发嗦啦”这样的信息,但演奏者照样能够准确找到每一个琴弦的每一个位置,这是为何?因为制造商已经在乐器硬件层面上设计好了,并且所有的演奏者都知道。本质是一种约定出来的共识! 硬件编址也是如此 我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么一根线,就能表示2中含义,2根线就能表示4中含义,依次类推。32根地址线,就能表示2^32中含义,每一种含义都代表一个地址。 地址信息被下达给内存,在内存内部,就可以找到改地址对应的数据,将数据在通过数据总线传入CPU内寄存器。

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)                                                地址总线上发出的地址信息 

5.1. 指针的内存布局

int a = 10; 
int *p = &a;
1. 这里定义了几个变量?在哪里定义的? 2. 一个整形,有4个字节,那么应该有4个地址!那么&a取了哪一个地址?那么如何全部访问这4个字节呢? 3. 如何正确的画出指针指向图?

1.两个,一个整型变量a,一个指针变量p。 

2.取的最低的地址,访问就是从最低的地址连续访问4个字节。(int)

3.如下图: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

5.2. 指针的解引用

我们可以通过这些图看理解代码的意思: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

那又有一个问题:*p用的是左值还是右值?

int a=10;        int*p=&a;

*p:*是一个操作符,*p的那一个表达式,*p使用的是左值还是右值?

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

(看下图)虽然我们把地址放到了变量p里面,但是在我们用的时候是直接访问该变量里面的地址,是直接访问目标地址的。所以右边的式子是等于左边的(上面的图)。 所以我们对指针解引用相当于使用的指针变量的右值(地址;内容),所以说对指针变量解引用的时候,是直接访问该指针变量里面保存的地址所指向的变量就可以了。其实也就是之前说的:在同类型情况下,对指针解引用,代表指针所指向的目标(a的值,10)。(当然这里说p也是直接访问地址,那为什么还要p呢?其实就是为了方便书写,额,可能还有好理解!) 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇) 这种情况也和上面是一样的: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

什么是直接访问和间接访问?

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇) 

5.3. int *p = NULL *p=NULL的区别

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

p是变量,在这里充当左值也就是说把NULL放进p的空间,即p的值从01改为了全0。 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

此时的*p为左值,充当a的空间。 

PS: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

但是含义不同(类型不同)。所以使用不准确可能会有报警。 

5.4. 如何将数值存储到指定的内存地址 

知道了指针的本质就是地址,地址就是数据,那么我们可以直接通过地址数据对变量进行访问吗? 题外话:大部分技术书,一定是落后于行业的。这本书也是,目前主流的编译器和操作系统,为了安全,已经有了很多内存保护的机制。我们目前的win和Linux都有栈随机化这样的机制来方式黑客对用户数据地址进行预测。当然,还有其他的栈保护机制,比如“金丝雀”技术之类的。 经过试验,目前vs2013和Centos7上,使用C语言定义的局部变量,在每次运行的时候,地址都是不同的。经过试验发现, 定义全局变量,每次更改代码,地址也会发生变化。所以这个实验没法正确做出来,但是程序崩溃,也能说明问题。 //demo #include<stdio.h> #include <windows.h> int main () { int a = 10 ; // 假设 a 变量的地址是 0x12345678 ,那么访问 a 变量,还可以直接通过指针方式进行访问 printf ( "%d\n" , * ( int* ) 0x12345678 ); // 本质是一种直接寻址的方式 * ( int* ) 0x12345678 = 100 ; // 本质是一种直接寻址的方式 // 所以, C 语言通过 int*p = &a; 这种指针变量的方式,访问目标数据有什么好处呢? system ( "pause" ); return 0 ; }

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

下面ok,在此我们就不需要关心a的地址是什么,所以指针的存在给我们栈随机化这样的技术存在了可能。我们只需要知道p指向a就可以了。所以用指针比直接用地址要好。 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇) 

5.5. 编译器的bug

看懂xia述代码,书中想表达的意思也就清楚了。 《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

三步调试的结果: 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

再看:

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇) 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)  

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇) 

p开始为0,p第一次指向自己,第二次把值改为10,第三次把值改为20。 

《C语言深度剖析》第四章 指针和数组 p1 C语言从入门到入土(进阶篇)

 

今天的内容就到这里了哈!!!

要是认为作者有一点帮助你的话!

就来一个点赞加关注吧!!!当然订阅是更是求之不得!

最后的最后谢谢大家的观看!!!

你们的支持是作者写作的最大动力!!!

下期见哈!!!

上一篇:链表的一些基本操作


下一篇:JAVA 反转链表