学习C语言之 了解指针

指针

1、指针是什么

        指针就是地址。

        平时口语中的指针,通常指的是指针变量,即用来存放指针的变量,也就是用来存放地址的变量。

        多字节变量取地址得到的地址是它的第一个字节的地址。

        指针变量在32位平台上是4个字节,在64位平台上是8个字节。以32位平台为例,一个地址是32个比特位,一个字节可以存储8个比特位,所以需要4个字节来存储。

演示如下:

int main()
{
	int a = 1;
	int* pa = &a;

	printf("%p\n", pa);		//打印结果 004FF73C
	printf("%p\n", &a);		//打印结果 004FF73C
	printf("%p\n", *pa);	//打印结果 00000001。只是以16位进制打印了1。
	printf("%d\n", *pa);	//打印结果 1
	//可以看出 pa 内存储的就是a的地址。
	// pa就是指针变量。指针是pa内存储的数据 004FF73C
	//*pa(解引用)得到的就是pa。
	

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* pb = &arr[2];

	printf("%p\n", arr);
	printf("%p\n", pb);

	return 0;
}

      

2、指针和指针类型

        指针类型决定了指针解引用的时候权限有多大。

        指针类型决定了指针走一步能走多远(步长)。

演示如下:

int main()
{
	int a = 0x11223344;		//0x11223344 是16进制数,十进制下是287454020
	int b = 0x11223344;		//0x11223344 是16进制数,十进制下是287454020
	int* pa = &a;	
	//进入调试页面可以看到 pa 的值是 0x004ffd84 十进制为5242244
	char* pb = &b;	
	//进入调试页面可以看到 pb 的值是 0x004ffd78 '\x1' 十进制为 5242232
	//即pb只占int b地址的第1个字节。

	*pa = 0;
	*pb = 0;
	//进入调试页面可以看到pa的值变为 0 。
	//进入调试页面可以看到pb的值变为 0'\0' 。
	//进入内存可以看到地址pa上的值变成00 00 00 00 。
	//进入内存可以看到地址pb上的值编程00 33 22 11 。

	printf("%d\n", a);	//打印结果 0 。
	printf("%d\n", b);	//打印结果 287453952 ,16进制是11 22 33 00。
	//可以看到char*的权限比int*的权限小,只改变了b的最低位4字节的44。

	int c = pa + 1;	
	//进入调试看到 c 是5242248 (上述十进制+4)
	int d = pb + 1;
	//进入调试看到 d 是5242233	(上述十进制+1)
	//可见char* 的步长为1,int*的步长为4。

	return 0;
}

3、野指针

        野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

        1)野指针成因

                a、指针未初始化

演示如下:

int main()
{
	int* pa;
	*pa = 0; 
	//报错,因为pa未初始化,指向是随机的。
	//部分编译器能执行,但也有其他问题。
	return 0;
}

                b、指针越界访问

演示如下:

int main()
{
	int arr[10] = { 0 };
	int* pa = arr;
	printf("%d\n", *(pa + 10)); //打印结果 -858993460。
	//指针越界,数组最后一个元素地址是 pa+9 。
	//有些编译器会报错,无法执行。

	return 0;
}

                c、指针指向的空间释放

演示如下:

int* test()
{
	int a = 0;

	return &a;
}

int main()
{
	int* pa = test();
	*pa = 0;	
	//a的地址空间已经被释放,此时是非法访问,容易出BUG。
	
	return 0;
}

        野指针属于编程陋习,尽量避免使用!

        2)如何规避野指针

                a、指针初始化

                b、小心指针越界

                c、指针指向空间释放及时置NULL

                d、避免返回局部变量的地址

                e、指针使用之前检查有效性 

演示如下:

int main()
{
	int* pa = NULL;
	//不确定地址的时候,可以给指针赋值NULL。
	//指针初始化NULL相当于 int初始化0;
	//指针指向的空间释放后可以及时置 NULL。
	//需要使用该指针的时候先判断一下。
	int a = 0;
	pa = &a;
	//判断指针是否为NULL,不为NULL再使用。
	if (pa != NULL)
	{
		*pa = 1;
		printf("%d\n", *pa);	//打印结果 1 。
	}


	return 0;
}

如上,做到使用指针时初始化,如int a = 0,int * pa = &a,没有变量时,可以先用NULL初始化。使用指针时则先确定指针不为NULL。这样基本可以规避大部分野指针。

4、指针运算

        1)指针 + - 整数

        2)指针 - 指针

        3)指针的关系运算

演示如下:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* pa = arr;
	int* paend = &arr[9];
	printf("%d\n", *(pa + 2));	
	//打印结果 3。即指针+2 指向的是第3个元素。

	printf("%d\n", paend - pa);
	//打印结果 9 。即指针相减得到的是元素个数。
	//同类指针相减才有意义。

	if (paend > pa)
	{
		printf("关系运算");	//打印结果 关系运算。
	}
	//数组地址由低到高,最后一个元素地址比第一位元素地址高。
	//可以看到,指针之间可以进行关系运算,判断大小。

	return 0;
}

 

5、指针和数组

        数组名表示的是数组首元素的地址(例外情况初识数组中已说明)。

        可以直接通过指针访问数组。

演示如下:

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%p\n", arr);		//打印结果是 007DF9F4
	printf("%p\n", &arr[0]);	//打印结果是 007DF9F4
	//可见数组名是数组首元素地址。

	int* pa = arr;
	printf("%d\n", *(pa + 1));	//打印结果 1。
	//可见通过指针直接找到了数组第二个元素。

	return 0;
}

6、二级指针

        指针变量也是变量,这个指针变量存放的指针就是二级指针。

演示如下:

int main()
{
	int a = 9;
	int* pa = &a;
	int** ppa = &pa;	//ppa 就是二级指针,即存放指针变量的指针。

	printf("%d\n", **ppa);	//打印结果是 9 。
	
	return 0;
}

7、指针数组

        指针数组是存放指针的数组。

演示如下:

int main()
{
	int* parr[5] = { NULL };
	//parr[] 就是指针数组,数组里存放的是指针。

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值