从新认识C语言指针(上)

​ 首创性并非首次观察某种新事物,而是把旧的、很早就是已知的,或者是人人都视而不见的事物当新事物观察,这才证实是有真正的首创头脑 —尼采java

<p align="center">本文已经收录至个人GitHub,欢迎你们踊跃star 和 issues。</p> <h3 align="center"><a href="https://github.com/midou-tech/articles" target="_blank">https://github.com/midou-tech/articles</a></h3> <h4 style="color:red;text-align:center">点关注,不迷路!!! </h4>mysql

序言

 指针是C语言学习者绕不过的一道坎,也是C语言学习者不得绕过的一道坎。辨别一我的C语言学的好赖就看他对指针的理解怎么样。指针内容也是工做面试常常问到的问题。本文将带你从新认识那个绊倒你的指针,以解你们的心头之惑(恨)。linux

为何要学习指针?

 有同窗就要说了,既然指针这么难,这么不通俗易懂,为何要学习他呢?其余高级语言都是把这块基本屏蔽掉了,不在让程序员直接操做指针,这里不直接操做指的是不让程序员用指针进行运算和强转而不是完全没有了。举个java的例子nginx

Object obj= new Object();
Object sec= obj;
sec = new Object();

 若是你去仔细研究他们的行为,就会发现 obj, sec 都只是一个指向对象的东西,能够为空,也能够修改指向,因此它们其实都是指针,只是 Java 的教材里面不在去提这东西而已,具体缘由看我后面讲解便知道了。git

​ 继续说为何学习指针,为何学习指针就必需要说到指针的优势了。程序员

  • 指针能够直接操做变量地址,因此很灵活。
  • 指针操做会减小不少变量的拷贝使得程序性能提高。
  • 能够动态分配内存。

 这些优势使得不少后台性能要求很高的系统、游戏内核、一些高并发的中间件都是使用C&C++语言开发出来的。好比强大的linux系统、nginx,mysql、redis等等。github

 曾经看到一个搞笑的评论,hhh面试

道生一,一辈子二,二生三,三生万物redis

电脑生汇编,汇编生C , C生C++,C/C++生万物算法

指针是什么?

 其实指针看起来复杂,听起来复杂,学起来复杂,可是总结下来指针究竟是个啥,也就一句话。

<p ><h4 style="color:red;text-align:center">指针就是地址,指针变量就是一个存放内存地址的变量</h4></p>

<img src="https://i01piccdn.sogoucdn.com/db3bc129d2c88816"></img>

 你没看看错,是的就是这么简单明了。一般咱们说的指针就约等于说的是指针变量。

指针和内存地址的关系

 不少人不明白指针其实也就是不明白内存地址,因此要想明白指针必须先明白指针和内存之间的关系。在讲内存和指针之间的关系以前先说下什么是内存。

 先明白一个问题,什么是内存?编程人员常说的内存指的是什么?

​ 内存是电脑的一个硬件组成部分。从单片机的组成咱们能够看到,CPU、内存和输入输出接口,就组成一个完整的电脑,其余通通属于外设。内存是能够被CPU经过总线进行操做的,也就是与CPU之间有总线相链接的。电脑全部的输入输出,都是要从内存来实现的。内存包括只读内存ROM和读写内存RAM,但在我的电脑(PC)中,咱们一般所说的内存,是指读写内存。

​ 程序人员常说的内存实际上是虚拟内存,程序直接操做的是虚拟内存而不是真正的物理内存。

纳尼 程序都是操做的虚拟内存? 那虚拟内存是个啥东西?

 这里先给你们画张C语言程序的内存布局图。关于进程和内存管理会在后面的文章讲出来,记得微信搜索 龙跃十二 点关注。

内存布局图

 这个图很好的描述了内存地址的布局,指针变量里面存放的地址也就是这个内存地址。顺便说下啥是内存地址,用十六进制表示出来的一串数字编号(就比如你家的门牌号),只是这个数字是给内存标号的。32位系统下这个编号是4byte(32个bit)表示的,64位系统下是8byte(64bit)表示的。(这个小问题面试会被问到的)

如何使用指针?

指针的声明

int *p;
char *p1;
float *p2;

 声明仍是很简单,指针的类型 * 变量名便可声明一个指针变量。

int num = 5;
int *p = &num;

 此时就是一个int类型的指针变量指向一个int变量,画个图解释下。

 以很清楚的看到指针p存放着变量num的地址,咱们一般说指针p指向变量num,当p知道变量num以后,p就能够对变量num为非做歹了,好比

int main(){
	int num = 5;
	int *p = &num;
	printf("*p=%d,num=%d\n",*p,num);  //此时num的值就变为5
	p+=1;
	printf("*p=%d\n",*p);    //此时p指向了哪里?这句代码会不会报错?
}

指针的大小和类型

 从上面的声明实例能够看到我定义了三种类型的指针,能够看出指针是有类型的。这里有同窗就有疑问了,不是存放内存地址的么,内存地址不就是一串十六进制表示的数字么(其实底层都是二进制),哪来的什么类型一说呢,为何又须要类型呢?

 这个疑问很好,我当时学习的时候也是很疑惑。首先咱们明白了指针是一个存放地址的变量,明白这点还不够还必须理解另一个问题就是

字节(Byte)是用于计量存储容量的一种单位,每个字节由8位组成(1Byte = 8bit)。地址能够理解为在一片内存中,每一个字节(Byte)的编号。

 因此不少人确定会明白了,指针存放的是一个变量的首个字节的地址,那么问题来了。

int a = 5; 
int *p = &a;

 咱们声明指针p指向变量a的地址,也就是说指针p里面存放着变量a的首地址,在32位平台下,int a 是4字节,指针去取a的值的时候找到的是a的首地址,那怎么拿到变量a,聪明的同窗已经恍然大悟,是的,没错,因此咱们的指针须要类型的,编译器去取指针指向的内容时候会根据指针的类型去取。画个图以下

 此刻我相信你对指针已经有了很高的理解了。指针的大小很好理解 就是存放地址的范围,地址的范围是操做系统地址线的根数决定,因此指针的大小是随操做系统的寻址范围决定的,通常32位系统地址总线也是32根,寻址范围是2^32次方

 顺便说下32位操做系统和64位操做系统的区别在哪里,系统的位数表明运算能力,所谓32位就是能计算的字长是32位的,64位系统能计算的字长是64位。处理器的字长越大,说明它的运算能力越强。

<h5 align="center">点赞👍 关注❤️ 走一波,精彩内容不错过</h5>

指针的操做符

 目前咱们已经了解到指针的解引用 *p 和 & 两个操做符,下面将带你了解更多的操做符。

操做符 名称 含义
*p 解引用 取出指针对应的内容
-> 指向 用来访问指针引用的字段
+ & += 加 和 加等 指针的加法
- & -= 减 和 减等 指针的减法
== 等于 比较两个指针
!= 不等于 比较两个指针
> &>= 大于 和 大于等于 比较两个指针
< & <= 小于 和 小于等于 比较两个指针

​  不知道还记不记得上面代码中的这个例子。

 这里使用到了两个指针的操做符,*p 和 p+=1。重点说下p+=1,他表明的是p=p+1,p指针此时指向了num变量的最后一个字节的下一个地址,指向了一个未声明的内容。不知道你们有没有尝试那句代码会不会报错?

 仍是要多练习一下的,说下结果,那句代码不会报错,会很顺利的打印出一个内容。是否是好危险,这个内容明明不是你的,是的操做指针就是很危险,可是同时也会很灵活。(世界就是这样子你获得一些东西就会失去一些东西。此时你获得了指针的高效,灵活,你就必须去平衡一些其余问题。还有一个很明显的例子,算法中经常面临到时间和空间的平衡问题。)

 平时用到最多的是前面六个,含义上面表里已经罗列,我重点介绍下+和-操做符。指针的+1表明指针的位置移动指针对应大小个字节。看个图,你们确定瞬间明白。

多级指针&多级指针的解引用

 看到这里,我相信聪明的你们对一级指针有了很明确的认识了,接下来带你们了解下多级指针和多级指针的解引用问题。不少人看到多级指针这就想放弃了,可是龙叔我就想说

把握一个核心本质就是,指针就是地址,指针变量就是存放地址的变量 多级指针无非就是饶了几个弯子,本质没变的。例如

int num = 9;
int *p = &num;
int **sp = &p;
int ***ssp = &sp;

 没错吧,本质没变的,就是饶了几个弯子。因此学习这件事情必定要把握本质的东西,那些变着花样也不过如此,岂能难道我辈。

 固然这只是举例子,多级指针很明显不是这种用法,这样用符合逻辑,可是没啥意义。看个多级指针的示例

int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
int **p = arr;
//此时要操做该二维数组,彻底可使用指针便可,不少时候减小了数据拷贝。
//在多维数组,字符串,复杂数据结构使用上,多使用指针,会减小不少数据拷贝。

 说到使用指针,减小数据拷贝,我就想到经历的一个bug,差点线上瘫痪。

工做case

 写了一个简单的代码,展现下伪代码。

bool fun(string a,string b){
  //对a ,b字符串进行一通操做,逻辑彻底没问题
}

 就这样简单的一个功能函数,写完,本身测试ok,还让同事review了代码。彻底没问题,大概十二点左右我发布上线了,通常发布以前会小流量灰度,发布也是轮流上线,这些都ok。可是晚上九点多,我意外收到报警了,内存瞬间飙升95.7%,我没有丝毫犹豫,我本身今天发布代码了,留了一台机器现场,其余立马回滚,一通操做。老大跑过来一块儿review代码,观察监控数据。老大仍是厉害,扫了一眼代码,立马发现问题,你这里函数参数为啥不使用引用或者const char* 类型,你把这个改了本身压测下。

 错误很明显,就是没用指针致使函数参数数据拷贝,在晚上流量高峰的时候,内存被瞬间抽干。这样的例子看起来很简单,咱们平常写代码时候可能不会太在乎,可是在处理高并发、强实时的问题上必定要慎重处理。

 今天就说到这里吧,下一篇给你们讲解指针的特性和指针的安全。

<h4 style="color:red;text-align:center">求点赞👍 求关注❤️ </h4> <h4 style="color:blue;text-align:center">「转发」是明目张胆的喜欢,「在看」是偷偷摸摸的爱。</h4>

若是有人想发文章,我这里提供<font face="宋体" color=blue size=4>有偿征文</font>(具体细则微信联系),欢迎投稿或推荐你的项目。提供如下几种投稿方式:

  • 去个人github提交 issue: https://github.com/midou-tech/articles

  • 发送到邮箱: 2507367760@qq.com 或者 longyueshier@163.com 或者 longyueshier@gmail.com

  • 微信发送: 扫描下面二维码,公众号里面有做者微信号。

精选文章都同步在公众号里面,公众号看起会更方便,随时随地想看就看。微信搜索 龙跃十二 或者扫码便可订阅。

<p align="center"><image src="https://tva1.sinaimg.cn/large/006tNbRwly1galsp9a07kj30p00dwae3.jpg" ></image></p>

相关文章
相关标签/搜索