什么是指针?
首先,要知道,计算机上CPU在处理数据时,会向内存读取数据,处理后的数据也会放回内存。那么内存里的空间是怎样管理的呢?
其实是内存空间被划分为一个个内存单元,每个内存单元的大小为1个字节,也就是8个bit。(1个bit可以放)。每个内存单元都有一个编号,有了编号,CPU能很快找到对应的内存空间。呀就是说,内存单元的编号,其实可以理解为门牌号,只不过在代码之中,被称作指针。
即 指针 ==地址==内存单元编号。在代码中,这样写:int * p, char * p, float * p......而p,就是指针。
其次,硬件CPU和硬件内存有大量数据交互,两者之间有很多线,其中一组线,专门用来传输地址,叫地址总线。32位机器就有32根地址总线,每根线只有两种状态,表示0或1。一根线有2种含义,32根线就有2^32种含义,每一种含义,都是一个地址。
最后,补充地址信息(指针p)下达给内存,在内存上找到地址对应的数据后,通过数据总线将数据传入CPU寄存器。
指针变量、取地址操作符&、解引用操作符*
在C语言中,创建一个变量,其实是在向内存申请空间。我们用&取出地址,用*来找到地址所指向的值。形象一点,就是拿着门牌号(p,也就是&a),找到房屋内的东西(*p,也就是a的值)。

指针变量类型的作用
在32位平台,所有数据指针均为4字节;在63位平台,所有数据指针均为8字节。
无论地址指向的是int(通常4字节)还是char(通常1字节),地址值本身的长度是固定的。

虽然指针不管什么类型,所占的空间大小都是一样的,但是,指针的类型,决定了
指针解引用的时候有多大权限。


int*的指针的解引用能访问4个字节(全部变成0),char*的指针解引用只能访问1个字节。
补充:在小端序(如x86、ARM等),低地址存低字节;在大端序(嵌入式、早期架构、某些网络协议),低地址存高字节。
再来看一个

结论:指针的类型决定了指针向前或向后走一步有多大。
void*指针
void* 指针 ,是一种无具体类型的指针,也可以成为范指针,可以用来接收任何类型的地址,但是不能直接进行指针的+-整数和解引用运算,也就是不能用(void* p)*p 进行运算。
指针运算
有三种。指针+-整数,指针-指针,指针的关系运算。
1.指针+-指针
数组在内存中是连续存放的,只要知道第一个元素,就能顺藤摸瓜找到后面所有的元素。

2.指针-(减)指针
前提条件:两个指针指向的是同一块空间

3.指针的关系运算
指针之间可以用关系运算符比较:<, <=, >, >=, ==, !=。比较的是之阵中存储的地址值(内存位置的大小关系)

为什么叫关系运算?
关系运算符,用于比较两个值的大小或者相等关系,当操作数是指针时,比较的就是内存地址的大小。
野指针
一种所指向的位置不可知的指针,也就是空有门牌号却找不到对应的空间。野指针的成因有三:一是指针未初始化,二是指针越界访问,三是指针指向的空间释放,如图。

那么如何规避野指针
1.对指针进行初始化
明确直到指针应该指向哪。如果现在还不知道指向哪里,就初始化NULL。对应头文件《time.h》。int* p = NULL。形象化理解:小孩p手里被塞了一把打不开任何门的钥匙NULL,安静的看着大人们(代码)工作,直到大人(代码)想起她,给她一把真钥匙,让她开门,参与工作。
2.小心指针越界
指针访问对应的空间,不能超出范围访问,超出了就是越界访问。
3.避免返回局部变量的地址
如上图。
const修饰指针
int const a = 10------即锁死a,使a具有常属性,但本质上还是变量,所以又叫常变量。
但是在C++中,const修饰a,a就是常量。
const a后,不能直接定义a,但是可以通过指针,间接地定义a。

const锁住它后面跟的东西
int const * p------锁死*p,即值,值可以不通过p而改,地址可改;图1
int * const p------锁死p存放的地址,地址不可改,但可改值;图2

图1

图2
assert断言
对应头文件<assert.h>,作用于全局,用于在运行时确保程序符合指定的条件,不符合就会报错且终止程序,因此有别称------断言。如果不想让assert工作了,就在开头写#define NDEBUG
好处在于,出现错误后,会爆出哪个文件哪以行出错;坏处在于,增加程序检查的额外时间。
其特性利于程序员排查问题,一般在debug中使用,在release中禁用就行。(vs的集成开发环境种,release直接优化了,于是即使release中有assert,也不影响用户使用程序的效率。
传值的调用和传址调用
strlen
求字符串长度的, strlen统计的是字符串中\0之前的字符个数,涉及头文件<string.h>。
函数原型:size_t strlen(const char* str)。参数接受一个字符串的首地址,然后开始统计字符串\0之前的字符个数,最终返回长度。
关键:
1.不计算\0
2.参数必须有效。
传递的指针必须指向以\0结尾的字符串
3.返回值类型。
是无符号类型,不要与有符号数混用
传值的调用和传址调用
传值调用:传给函数复印件,改复印件不影响原件。
传址调用:给函数原件的位置,直接操作原件。
用++两个整数交换数值++来说明。
传值调用:

可以看到,交换失败。
传址调用:
