C语言概括(指针)
一. 指针的概念
(1) 通俗的讲就是把内存想象成一个大走廊,走廊上有很多房间,每个房间的大小都是1个字节,每个房间上面都带有一个门牌号,从0开始,依次递增,这个房间号,就叫"地址",就可以使用另外一块内存空间来保存这个地址,那么这样的内存空间就称为"指针变量".
(2)概念:指针是一个变量,变量里面存了一个整数,这个整数具有特定的含义就是表示内存中的地址.
(3)指针变量涉及两方面的信息
- 这个地址从哪开始
- 这个地址有多长(这个体现在类型里,short对应的内存是2个字节;int对应的内存空间是4个字节)
注:两方面信息综合才能确定一个内存空间
(4)指针变量的长度:指针变量的长度是固定的,在32位Windows下,固定是4个字节,在64位Windows下,是8个字节;
(5)野指针:
如果给指针随便赋值一个整数,此时大概率是无效的地址,如果一个指针里面存的是一个无效的地址的话,此时就称这个指针是"野指针";对于野指针解引用会导致未定义行为.
(6)空指针(NULL):
0X0这个地址比较特殊,对于我们的代码中是一定无法使用这个内存的,就可以认为0X0是野指针的一个典型代表,就管他叫"空指针(NULL)",他实际是一个宏,本质上是一个0的整数.对于空指针解引用会导致未定义行为.
二.指针运算(弊大于利)
(1)&运算
&运算,其实获取到的地址,是数值最小的这个地址
(2)*运算(解引用操作/间接访问/提领操作)
- 通过变量名获取变量内容,这样比较"直接",而"间接访问"是通过指针变量,先获取到地址,再根据地址找到对应的变量内容,这就没那么直接,这就叫做"间接访问";
- 解引用操作,重要的注意事项:
必须针对有效的内存进行解引用,一旦解引用了无效内存,就会发生未定义行为(你申请到的内存,就是有效的内存,你没申请就是无效的内存,创建变量就是申请内存);eg:野指针和空指针都不能解引用 - 指针类型其实就是在描述针对指针解引用之后,得到的类型是什么
- 解引用时需要先找到对应的内存的起始地址,然后再获取到该内存长度,两者都得到了,才能真正读取这个内存的数据
(3)指针加减整数 - 指针+1并不是单纯的地址+1,而是地址要加上一个值,使这个指针能够指向下一个元素;指针-1也是同样的道理.
- 指针加减整数的用途: 用来操纵数组;
(4)指针减指针 - 大部分指针减指针都是无意义的,除非是两个指针指向同一个连续的内存空间(数组),此时指针相减才是有意义的.此时结果就表示两个指针中间隔了多少个元素(和指针类型密切相关),如果是两个不同类型的指针相减,编译器不会报错,但是结果无意义;
- 指针相减相当于是指针加减整数的逆运算.
(5)指针比较大小 - 对于==和!=来说,这个比较普适的,任意两个指针都可以进行这样的比较(主要用于跟空指针比较).
- 对于< >,<=,>=来说,这样的比较是需要前提条件的,两个指针要指向同一个连续的内存空间,本质就是在比较指针变量里面储存的地址的值的大小关系.
注: 比较< >这些比较少见,主要就是操作数组的时候或用到,而==和!=是非常常见的.
(6)指针[ ]运算
相当于是指针加减整数并且解引用的的简化写法
eg: p[i]等价于*p(p+i)
注:[ ]里面可以写负数,就相当于是指针的减法在解引用.
三. 特殊的指针void*
(1)概念:在c语言中有一种特殊的指针,只涉及到地址,不涉及大小,他就是void*,他并不是空指针,NULL才是空指针
(2)void的用处:假设我们需要写一个函数,这个函数需要同时兼容多种不同的的指针的时候,这个时候就可以使用void来解决了.因为不同类型的指针差别就在于"内存的长度",使用void就是暂时忽略了内存的长度.
(3)void不能解引用;void不能加减整数;void之间也不能相加减.
(4) 使用void的弊端:c语言不支持泛型编程,所以使用void只是一种比较粗糙的模拟实现泛型的方案,这个方案存在诸多弊端
(5)可以把各种不同类型的指针和void*之间进行相互赋值.
四.数组和指针
(1) 数组名在有些时候能隐式转换成首元素的地址
(2)什么时候能隐式转换
- 函数传参
- 参与运算
注:有些操作数组不支持,但指针支持,进行这样的运算,就会触发隐式类型转换:+ - 整数,==,!=,<,<=,>,>=,*这些都能触发.
数组和指针本来是完全不相干的事情,只不过c语言中有惊天BUG,导致指针和数组混了;
c中的数组一言不合就隐式转成指针;
c中的指针又能[ ]搞的跟数组似的;
(但要注意如果让说数组和指针有什么区别的时候,这句话本身就是错的,因为数组和指针本来就是完全不相干的事情,何谈什么区别)
(3)指针数组和数组指针 - 数组指针:int(*)[4] (本体是一个指针)这是一个指针,指向了一个长度为4的int数组
- 指针数组:intarr[4] (本体是一个数组)这是一个长度为4的数组,每个元素是一个int
五.二级指针
(1)本质上就是一级指针,只不过指向的元素又是一个一级指针
p2就是二级指针,针对p2解引用得到的就是一级指针的值(int类型的)也就是p内部储存的值.
(2)一维数组进行函数传参的时候,就会隐式转换成指向首元素的指针(int[ ] => int)
二维数组,进行函数传参的时候,也要隐式转成指向首元素的指针;例: int[3][4]得到的指针是一个指向长度为4的一维数组的指针int(*)[4]
六.const和指针
(1) const是一个关键字,用来定义一个常量
(2)const int* p = &a/int const * p = &a(p是不能被修改的)
(3)int const p = &a (p的本身的值不能被修改)
(4)const int* const p = &a(都不能被修改)