初学者学指针

前言

作为一名小白,了解了基本的指针,在此,我将我在学习指针过程中,遇到的重难点分享出来。

(一)指针运算

指针运算一般运用于数组。因为数组在内存中是连续存放的,这时候进行指针的运算才有意义

1.指针变量与整型运算 

下面以整型数组为例(代码有省略)

int arr[6] = { 1,2,3,4,5,6 };

 取出首元素地址存入到指针变量p中

int* p = &arr[0];

 一下代码在某次运行后得到了注释中的结果

printf("%p\n", p);    //000000BB53AFF848
printf("%p\n", p + 1);//000000BB53AFF84C
printf("%p\n", p + 2);//000000BB53AFF850
printf("%p\n", p + 3);//000000BB53AFF854

由此可见,直接对指针变量p(int*类型)进行+1的操作,会得到p所存地址+4个字节后的地址。

那么,假如指针变量p是char*类型,p+1所得结果也是p所指向对象的地址加上4个字节吗?

初学时,我也有这样的误解,因为无论是char*还是int*这样的指针变量,它们在内存中的大小都是4或8个字节p+1结果为,p所指向对象的地址加上4个字节似乎有点道理。

char str[] = "abcdef";
char* p = &str[0];

printf("%p\n", p);    //000000186514FA84
printf("%p\n", p + 1);//000000186514FA85
printf("%p\n", p + 2);//000000186514FA86
printf("%p\n", p + 3);//000000186514FA87

为了验证,我用char*类型的指针变量p做了实验,当指针变量p+1,结果是p所指向对象的地址+1。

由此,可以推断出结论,一个指针变量+1,结果与指针变量的类型有关。char类型是一个字节,那么char*指针变量+1结果就加上1个字节,int类型是4个字节,int*指针变量+1结果就加上了4个字节。

2.指针变量与指针变量运算

指针变量与指针变量相减会得到什么结果?

还是用数组举例。

指针与指针变量相减,相当于两个地址相减,若用整型来返回他们相减的值,过程如下

int arr[6] = { 1,2,3,4,5,6 };

int* p1 = &arr[1];
int* p2 = &arr[2];

printf("%d", (int)( & arr[1] - &arr[2]));//-1

 可见,数组中两个元素的地址相减,返回整型,结果的绝对值,为它们所相差的元素个数。

3.指针变量大小比较

指针变量也可以比较大小

int arr[6] = { 1,2,3,4,5,6 };

int* p1 = &arr[1];
int* p2 = &arr[2];

if (p1 > p2) printf("1");
else printf("2");
//结果为2

 指针变量比较大小,即比较地址的大小

(二)指针数组&数组指针

1.指针数组

指针数组,与整型数组,字符数组等其它数组一样,是一种数组。每个元素的类型是指针。

int arr1[3] = { 1,2,3 };
int arr2[3] = { 2,3,4 };
int arr3[3] = { 3,4,5 };

int* p[3] = { arr1,arr2,arr3 };

如图所示,p就是一个数组指针,里面有三个元素,存放着三个整型数组的首元素地址,即这三个元素的元素类型都是int*。

打印三个整型数组里的所有元素就可以通过这个指针数组来实现

for (int i = 0; i < 3; i++)
{
	for (int j = 0; j < 3; j++)
	{
		printf("%d ", p[i][j]);
	}
	printf("\n");
}

p[i][j]看起来有点眼熟,很像二维数组的一个元素,实际上,通过p[i]访问到了三个数组的地址,数组地址再加上[j],就可以访问三个数组中的各个元素。所以,通过指针数组的方式,模拟出了二维数组。

2.数组指针

数组指针,与整型指针,字符型指针一样,都是一种指针。

定义

int arr1[3] = { 1,2,3 };
int arr2[3] = { 2,3,4 };
int arr3[3] = { 3,4,5 };

int(*p)[3] = &arr1;
int* p[3] = { arr1, arr2, arr3 };

为了方便比较,在此,我把数组指针,与指针数组都拿了出来。

不难发现,先用圆括号()把*p括起来,说明p是一个指针变量,指向的类型为int [3],即一个数组。

而不用圆括号()时,p会先于方括号[]结合,此时p就成了一个数组名,该数组每个元素的元素类型为int*类型。

数组指针有什么作用呢?

作用

void f1(int a[][3])
{

}

void f2(int(*p)[3])
{

}

int main()
{

	int a[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
	f1(a);
	f2(a);

	return 0;
}

在上面的代码中,f1后的圆括号()中放的是二维数组的数组名,传过去的参数为二维数组的首元素地址,即一整个一维数组a[0]的地址,该地址的类型就是数组指针类型。

所以我们可以将函数的参数部分写成f2的形式,即用一个数组指针接收实参。

(三)函数指针&函数指针数组

1.函数指针

在了解函数指针前,我们先回顾一下数组指针

int arr1[3] = { 1,2,3 };

int(*p)[3] = &arr1;

数组指针与数组定义时有相通之处,即把定义时的数组名替换为*p

那么函数指针也是同样的道理

定义

int f1(int x)
{
	return x + 1;
}

int main()
{
	int (*p)(int) = &f1;
	return 0;
}

把定义函数时的函数名换为(*p),此时的p就是一个函数指针变量

调用

int f1(int x)
{
	return x + 1;
}

int main()
{
	int (*p)(int) = &f1;

	printf("%d\n", (*p)(5));//6
	printf("%d\n", p(5));   //6

	return 0;
}

无论是*p还是直接使用p,都能调出函数的地址,实际上,&f1与f1也都是函数的地址

2.函数指针数组

定义

int f1(int x)
{
	return x + 1;
}

int f2(int x)
{
	return x - 1;
}

int f3(int x)
{
	return x;
}

int main()
{
	int (*p[3])(int) = { &f1,&f2,&f3 };
	return 0;
}

在p后加上方括号[],此时的p就是一个函数指针数组的数组名

每个元素的类型为函数指针int(*)(int)

调用

int f1(int x)
{
	return x + 1;
}

int f2(int x)
{
	return x - 1;
}

int f3(int x)
{
	return x;
}

int main()
{
	int (*p[3])(int) = { &f1,&f2,&f3 };

	for (int i = 0; i < 3; i++)
	{
		printf("%d ", p[i](5));//6 4 5
	}
	return 0;
}

以i=1为例,p[1]调用出了函数地址(函数名),再加上(5),就相当于使用了f1(5),即调用了函数。

(四)回调函数

回调函数,指的是一个函数a,它的地址以函数指针变量的形式被传给了函数b,当函数b中的函数指针变量被调用时,该函数指针变量所指向的函数a就是回调函数。

int a(int x)
{
	return x + 1;
}

void b(int (*p1)(int))
{
	int ret = a(5);
	printf("%d\n", ret);
}

int main()
{
	int (*p)(int) = &a;

	b(p);

	return 0;
}

如图就是一个例子

(五)总结

该博客介绍了指针基本相关的一些易混淆的概念。到此结束,谢谢浏览

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值