指针
指针的概念
指针实质上就是内存地址,指针变量就是用于存储变量的地址
指针的声明
数据类型* 变量名;
如:
int num; //定义一个整型变量,变量名为num
int* pNum; //定义一个整型指针变量,用于存储一个整型变量的地址
注意事项:
(1)指针的数据类型取决于所保存的地址上的数据类型
(2)声明指针变量的时候,*表示变量是指针,不可以省略
指针的初始化以及赋值
int i;
int* pi; //野指针
int* pi = NULL; //空指针
int* pi = &i;
=> int* pi; pi = &i;
pi = &i;
*pi = *(&i) = i;
指针作为函数的形参
如:void fn(int*p){}
int num = 66;
fn(&num);
指针作为函数的返回值
注意:
永远不要返回一个局部变量的地址,因为局部变量在函数结束之后,所使用的内存会被释放
指针和数组
笔试题:
void fn(int a[10])
{
sizeof(a) = 4; //a相当于是一个指针
int b[10];
//数组名是数组的首地址,也就是数组中第一个元素的地址,并且数组名是个常量,不可改变
sizeof(b) = 40; //数组元素的大小 * 数组元素的个数
}
讨论:为什么数组作为函数参数的时候就会退化成指针呢?
首先先理解以下内容
1、 一维数组与指针的关系
一维数组就是指在栈上定义大小为 数据类型*数据个数的内存空间
如:
Int num[5]; 代表定义了一个整型数组 大小为sizeof(int)*5 = 20
再看
保存普通数值的变量,在C语言中就用基本数据类型进行了定义,但是由于保存这些变量都有固定的地址(该地址是系统自动分配,也可以是程序员手动分配),根据需要,我们就得保存起来这些地址,虽然这些地址的实质就是一个普通数值(16进制表示),但是假若都用基本数据类型就无法区分这些数据与地址,所以就用了另一个方式来表达,就不允许用之前方式进行保存,而这个方式称为指针,并且提供机制(*地址)表示该地址上的数值
Int data = 0;
Int *p = &data;
表示在栈上定义了一个能保存int数据变量地址的变量p,变量p也是有地址的,即&p,并把data的地址复制一份给p,假若data的地址是 1 (16进制表示0x01)
那么p的值就是1(16进制表示0x01)
Int num[5] = {1,2,3,4,5};
Int* p num= num;
num是指num这个数组第一个元素的地址,即&num[0],&num表示整个数组的地址,虽然&num[0]和&num的数值相同
以上pnum 保存的是第一个元素的地址,假若&num[0]值为0x05
注意:&num +1 若num 末尾地址是0x20 那&num +1 等于 0x21
num+1 若num开头地址是0x10 那么num +1 等于 0x14
为什么不可以(int* pnum = &num)明明地址相同,为什么能进行pnum+1,并且pnum+1 ==num[1];
原因:
因为int* pnum, pnum只能保存一个整型变量的地址,而&num代表整个数组,虽然地址一样,但是保存长度都不一样了,pnum只能保存四个字节的变量地址,而&num的表示的长度明显大于4;为什么能pnum+1,因为+1 由pnum当前地址向后面移动4个字节,他的结果还是一个四个字节的变量地址,所以可以进行
2、 指针数组与数组指针
那么&num用什么进行保存呢
再看,int *po[5];
Int (*pt)[5];
根据运算符的优先级解释,
int *po[5]; 【】的优先级高于* 解释为po是一个长度为10的数组,大小为20,而这个数组都是整型指针,每个指针都能保存整型变量地址,即为指针数组
首先是一个数组,然后数组中每一个元素是个指针
而 Int (*pt)[5];
根据优先级解释,()优先级大于【】,解释为pt是一个指针,这个指针大小为4(因为是整型);而这个指针保存的类型是长度为5的整型数组,即数组指针,只有一个指针,指针保存的是整个数组地址
所以
Int (*pt)[5] = #
3、二维数组表示与数组指针
Int hu[3][2];
表示定义3行2列的二维数组,hu代表第一行地址,注意,这个第一行地址是不是第一个元素hu[0][0]地址,而是包含hu[0][0],hu[0][1]的整个地址,换句话说是整个一维数组的地址,虽然第一个元素地址与第一行地址的数值相同
所以
Int (*pw)[2] =&hu[0];
由于&hu[0] 等价于 hu ,所以
Int (*pw)[2] =hu;
pw+0 == pw[0];
pw+1 == pw[1];
pw[0] = hu;
pw[1] = hu+1;
最后回归讨论:
例子:
Void foo(inta[],int len)
{
sizeof(a)= 4; //a相当于是一个指针
int b[10];
//数组名是数组的首地址,也就是数组中第一个元素的地址,并且数组名是个常量,不可改变
sizeof(b) = 40; //数组元素的大小 * 数组元素的个数
}
void Main()
{
Int data[5] = {1,2,3,4,5};
foo(data,5);
}
且看,foo传值,我们知道data代表 &data[0],即传过去的是一个 整型变量的地址,不是整个数组地址,那么函数foo就形式参数的类型就必须是int * ,因为这样才能保存实参传过来的,而为什么写成int a[],或者int a[5],那是因为方便与观看,C标准允许这样的书写,很简单就能看出传过来的有什么,就是这样的书写引起了很多程序员的错误理解,他们都以为传过来是整个数组,所以计算的结果就等于20。但函数只是接收了数组中第一个元素的地址,通过地址就可以遍历数组,所以计算结果就是4