关于指针变量(详细)

本文详细介绍了指针变量的概念、创建、访问、更改、大小、const修饰、运算、野指针的处理、数组与指针的关系,以及函数指针等内容,帮助读者理解指针在编程中的重要性和使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

注:本文 设p为指针变量,进行讲解

一、指针变量就是存放地址的变量

<1>、通俗讲:

<2>、指针变量建立

<3>、理解

二、指针变量的访问以及对指针的更改

1、更改地址

2、访问指向的内容

三、指针变量的大小和地址的组合个数

1、大小:

2、组合个数:

四、关于const(锁住,常数性的意思)

1、修饰变量

2、修饰指针变量

1)、限制指针变量本身

2)、限制指针变量指向的内容: 

3、总结:

四、指针的特征:

1、p自己本身就是一个地址

2、可以存放一个地址

3、可以指向一个值(*p——>a(访问a的内容))

五、指针的运算:

1、指针加减整数

2、指针加减指针的绝对值

3、指针的相关运算

五、野指针:

1、概念:

2、出现野指针的原因:

1)、未初始化

2)、指针越界

3)、指针指向的访问空间释放

3、如何避免:

1)、设置空指针:int *p = NULL;

2)、明确指针指向哪里,直接赋值或者初始化;

六、数组与指针:

1、数组名是首元素地址:

2、数组与指针的关系:

1)、(i表示第几个元素)

2)、数组传参:

七、二级指针:

八、指针数组:

九、字符指针:

十、数组指针变量:

 

十一、函数指针变量:

十二、函数指针数组:

十三、补充下指针类型:


注:本文 设p为指针变量,进行讲解

一、指针变量就是存放地址的变量

<1>、通俗讲:

        就好比如:变量是一个盒子,用来存放数值,可以随意替换和自由取出;指针变量则就是一个用来存放地址的盒子;相当于把变量盒子的地址给放到了指针变量的盒子里,我可以用指针的盒子种放的地址找到右边的盒子

<2>、指针变量建立

        如下:int *为类型,p为对象(变量)

be431b6b73db4d288bbdb0fc116372d3.png            

       初始化:取a的地址存入到 p 中,可以理解为 p = &a;

118bc54739924d4caeccc3e95bfcd906.png

<3>、理解

        *p 可以当做是a的别称,对*p做出修改,a的值也会发生改变;举个例子:高启强每次需要做一些“脏活”时,会对老莫说想吃🐟了,这时老莫执行这个指令,最终完成了这个任务,则最后我我们的结果就是解决了,那么相当于高启强的事情解决了,这件事👉由老莫来行动的,但是高启强也因此收益了;

二、指针变量的访问以及对指针的更改

        * 解引用操作符(间接访问操作符)——引用(访问)它指向的内容(通过这个*,我们可以追溯地址到所指向的位置)

>>>比如:学校要求班主任家访,她手上有一本花名册(“ * ”),写了每个同学地址,通过花名册(解引用“*”)就能找到每个同学的家,去同学家里。(举例子是为了便于理解)

1、更改地址:

12028536937a45ec83050d5f2aa0a7b8.png

如上图,取出b的地址,放入b中;从该图也可以看出 对象p 代表的是地址

访问地址时即可写成以下两种方式》》都是b的地址:(%p是表示转换地址的转换说明)

printf("%p\n",p);
printf("%p\n",&b);

2、访问指向的内容

int main()
{
	int a = 10;
	int* p = &a;//将a的地址存入p(指针)中
	*p = 20;//将a的内容改为20
	printf("a = %d\n", a);
	printf("a = %d\n", *p);
	return 0;
}

a的值和*p的值相等,这里对p用了解引用操作符,就是说访问p所存放的地址里面的内容

三、指针变量的大小和地址的组合个数

大小和地址组合个数都与编译环境密切相关

1、大小:

        在32位环境下,指针变量大小为32个比特位即4个字节

        在64位环境下,指针变量大小为64个比特位即8个字节

2、组合个数:

        在32位环境下,地址线有32个,因为在计算机中内存以二进制形式存在,所以每一个地址线可以从0和1中选一个,则为2的32次方个组合方式;同理,64位下有2的64次方个组合方式。

四、关于const(锁住,常数性的意思)

1、修饰变量

int main()
{
	const int a = 10;//a具有了常数性
	int arr[a] = { 0 };//为何不能将a代入表示数组元素个数?因为a本质上还是变量

	return 0;
}

根据情况看:

c语言中a只是具有了常数性,不可更改,但是a的本质仍为变量,不可以当成常量来应用;

但是在c++中变量a被const修饰就是变成了常量

2、修饰指针变量

1)、限制指针变量本身

如下:const放在了*的右边,限制了p,因为p相当于&a也就是说限制了p中存放的地址只能是a,不可以改变p里面存放的地址,若选取下面的p = &b,vs中会报错,你也可以自行测试;但是*p=20可以改变你只是锁住了地址,并没有锁住那个地址里面的内容

int main()
{
	int a = 10;
	int b = 20;
	int* const p = &a;
	*p = 20;
	p = &b;
	return 0;
}

特别声明:

如下:利用指针变量则可以将const int a = 10,a=10变成a = 20;举例:高启强想要杀一个人灭口,但是他表面上被安欣监视了,这时他自己是不能去杀人的,会暴露,但是这个时候他可以让老莫代替他去杀掉那个人;下面程序也是如此,既然a不能改变,则可以利用*p来改变指向的内容,从而改变a的值

int main()
{
	const int a = 10;
	int* const p = &a;
	*p = 20;
	printf("%d", a);
	return 0;
}

6aa522152faf462cafed7a8088852dbf.png2

2)、限制指针变量指向的内容: 

如下,将const放在*右边,相当于const *p,锁住了*p指向的内容,但是存放地址可以改变(p没有被锁,它本身的功能就是存放地址)还一种写法:const int *p

int main()
{
	int a = 10;
	int b = 20;
	int const * p = &a;
	*p = 20;//出现报错,无法更改内容
	p = &b;//可以改变存放的地址
	printf("%d", a);
	return 0;
}

3、总结:

const在*的右边就是锁住 p 所存放的地址;

const在*的左边就是锁住 *p 所指向的内容;

四、指针的特征:

1、p自己本身就是一个地址

建立一个盒子也是需要占用内存的,它的建立本身就需要向栈申请一块内存

2、可以存放一个地址

指针变量是一个存放地址的盒子(它的功能)

3、可以指向一个值(*p——>a(访问a的内容))

指针变量存放一个地址,然后这个地址的里面内容也间接被套入盒子里面;通过*就可以去范文(*是打开指针变量种存放的那个盒子的钥匙

五、指针的运算:

1、指针加减整数

        如:p+1——>跳过了sizeof(type)*1的字节{type表示的是类型}

2、指针加减指针的绝对值

        相当于日期减去日期可以求出间隔天数,指针减去指针也能求出2个指针间元素的个数(但是两个指针要是指向同一个空间才可以),一般用于【*p = &arr(arr是数组)】数组元素个数计算

3、指针的相关运算

五、野指针:

1、概念:

        指针指向的位置是不可知的、随机的、不正确的,没有明确的限制的就是野指针;(一条没有绳子的野狗)

2、出现野指针的原因:

1)、未初始化

2)、指针越界

3)、指针指向的访问空间释放

        int *p = lest();错的;函数只有在使用时才会在栈区生成空间,结束就销毁了;就好比如我今天睡酒店,张三知道我的房间号,第二天我退房了,而张三想要直接来我的房间睡,找上门却没有进去睡的资格,因为从第二早上退房后,这个房间就不归我使用了。

(一般如果要用指针接受函数返回值的话,在被调用的函数里要用到动态内存空间的申请,因为你申请的动态空间是在堆区申请的,销毁的是函数的变量,动态申请的空间还是存在的)

3、如何避免:

1)、设置空指针:int *p = NULL;

2)、明确指针指向哪里,直接赋值或者初始化;


六、数组与指针:

1、数组名是首元素地址:(一般情况都是如此)

        arr = &arr[0];

        特殊情况:1)、sizeof(arr)——>单独一个数组名为整个数组;

                           2)、&   在arr(数组)前面,则取整个数组的地址;

举例: int arr[10]为例子,&arr+1———>跳过了40个字节(整形为4个字节*10个元素=40)

                                          &arr[0]+1(相当于&arr[1]的地址)——>跳过了4字节(&arr可以看出P,&arr+1则就是P+1,指针p为数组首元素地址,加1就是数组第二个元素地址)

2、数组与指针的关系:

1)、(i表示第几个元素)

*(p+i)== *(arr+i) == arr[i]==p[i ]  ==  *(i+arr);
p==&arr[0]    ==    p==arr替换上面arr即可得到;

注意数组是块连续存放的空间,指针变量就是一个变量,并不等于数组;指针变量只是存放了数组的地址

2)、数组传参:

        形参可以写成数组形式,但本质上是个指针变量;

        如:函数int add(int arr[]);我传参一个arr上去,传的就是arr[]数组首元素地址;

       当我们要求数组长度时(元素个数),注意要在main函数中计算后在传给函数,不要到调用的函数里面在进行计算

七、二级指针:

顾名思义就是用来存放一级指针的地址的指针;如果想要对一级指针的进行操作,就需要二级指针来接受一级指针的地址

int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	return 0;
}

关于地址:p存的是变量的地址,*p就是自己的地址

75c4da8166e7483b8d42613856145791.png

1bbd2302ec3c4a3c8ad95b5861ae3d00.png

八、指针数组:

本质是数组 ,是用来存放指针数组int *arr[]则arr先于[]结合(优先级),可以理解为为int * 的数组,

i表示二维数组行,j表示列

一般用来模拟二维数组;*(*(arr+i)+j)

二维数组的首元素为arr[0],表示第一行

*(arr+i)——>arr[i]        *(arr[i]+j)——>arr[i][j]

所以说可以通过指针数组模拟实现二维数组

九、字符指针:

例:char arr[] = {"abcdef};

       char * p = arr;//首元素地址;

打印时不要用解引用;占位符%s为打印字符串转换说明,只需要向%s提供首元素地址即可

若想要*p则就是提供首元素地址的内容,用%c来打印,打印的就是首元素的字符;

注意:将字符串直接赋值给字符指针的话,此时字符串为字符串常量,char * p ="abcde";无法在更改内容,因为常量字符串不可以被修改,常量字符串放在代码块区域

十、数组指针变量:

指向数组的指针——存放数组的地址;

用于二维数组传参,看下面笔记:因为二维数组可以将每一行看成一个一维数组,即每行有几个元素就是数组指针指向的元素个数

2253cf591e4e42dc9a3c1a7b689d3b2e.jpeg


要明确二维数组的列,因为指针存放的数组首元素地址,那么存放进去以后数组

 二维数组在内存中也是连续存放的存放的,如下可以理解为由元素个数为3的2个一维数组组成;

二维数组数组名也是二维数组首元素地址;这里我们将二维看成一维数组,那么第一行就是首元素

6985cbbcd68544419fe11e03b7ee3281.png

 

十一、函数指针变量:

专门存放函数地址的指针,在这里函数名&函数名都是函数的地址;具体如下:

使用时可以 p(x,y);传参数

2dc288f960514f868162e60030bb34d2.jpeg

十二、函数指针数组:

专门存放函数的数组(存放的是相同类型的函数):

e70b8344211e4908b93a95cc6e3ea286.jpeg

一般用于转移表的使用

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}
	} while (input);
		return 0;
}

十三、补充下指针类型:

  指针类型决定了指针进行解引用操作时,访问的字节个数(即访问权限

如:int* 访问的就是4个字节;char*访问的就是1个字节

int* + 1,则行走4字节          char * + 1,则行走1个字节

看下图:

pa与pb地址一样,当时pa加1与pb加1的地址就根据访问权限来计算了

40db77b256ca452c82e551831ce760ee.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值