【c语言学习】深入理解指针(1)

本文详细介绍了C语言中的指针概念,包括内存和地址的关系、取地址操作符&和解引用操作符*的作用,以及指针变量、void*类型指针、const修饰指针的特性。此外,还讨论了指针运算(如指针加减整数、指针比较大小)和避免野指针的方法。
摘要由CSDN通过智能技术生成

前言

相信大家在学习c语言的过程中都被指针所折磨过吧,的确,指针作为c语言学习的一大难点,确实比较难以理解,我在学习的过程中也是跟不上学校老师的思路,哇,这是什么,这地址那地址的,取地址又解引用的,真烦,后来经过一段时间的学习,对指针的理解也更加清晰,之前的疑惑也都解开了,那么本篇文章就我自己的理解给大家介绍一下指针。

内存和地址

讲到指针肯定离不开内存和地址,其实指针就是地址,可以把内存和地址分别理解为一个学校的宿舍,宿舍就是内存,给每一个宿舍标上门牌号,作为地址方便查找,也就是指针。
在这里插入图片描述
0XFFFFFFFF也就相当于门牌号,这些地址都被存放在内存单元中,一个内存单元也就是一个字节

取地址操作符(&)和解引用操作符(*)

&就是把内存中的地址拿出来
在这里插入图片描述
因为 int 类型占四个字节,我们通过&得到的为四个字节里的较小地址的编号,那么我们把地址取出来干什么呢,肯定是拿来用的,所以我们得先把地址存起来,那么用来存放地址的变量就称为指针变量

#include <stdio.h>
int main(){
int a = 10;
int* p = &a;
*p = 0;
printf("%d\n",a);
return 0;
}

p 就作为了指针变量来存储a的地址,类型为int* ,那么我们又怎么去使用地址呢,就需要用到解引用操作符 * ,代码中 *p就是通过指针变量p里面存放的地址找到指定的空间进行修改。
总结来说呢,取地址& 和 解引用 就是一对相反的操作,&把地址拿出来,解引用 就是顺着地址再找回去。

指针变量

在上面已经提到,指针变量就是用来存放指针的变量,那么既然是变量,大小又是多少呢。
在32位计算机下指针变量的大小为4个字节,64位时指针变量的大小为8个字节。
在这里插入图片描述
在这里插入图片描述
可以看出,无论前面定义的类型是什么,在X86环境下,指针变量的大小就是4个字节,X64环境下,指针变量的大小就是8个字节。
那么前面定义的类型又有什么意义呢
在这里插入图片描述
首先,pa和pc里面都存的是a的地址,所以输出结果相同,又由于指针变量类型不用,后面进行指针运算的时候,同时加一,但增加的字节不同,可以看出,char*类型的加了一个字节,int *类型的加了4个字节。

void*类型指针

在前面我们看到了int *,char *等一些指针类型,还有一种类型比较特殊

#incldue <stdio.h>
int main(){
int a = 0;
float b = 	0.0;
void* p1 =  &f;
p1 = &a;
}

void* 类型的指针可以存放其他类型的指针,但也因为这个原因,它不能像其他指针那样进行+1 , - 1等一些操作,因为不知道每次加减多少字节。

const 修饰指针变量

const 修饰指针变量时,可以放在*左边也可以放在右边,但所产生的效果是不相同的。

下面我们通过具体的例子来感受一下:

在这里插入图片描述
当我们把const 放在* 右边时,指针变量p就不能再指向其他地址了,编译器就会报错
在这里插入图片描述
但是我们却可以通过解引用,来修改值,我们再看下面一组例子:
在这里插入图片描述
当我们把const放在*的左边时,产生的效果就和上面的情况相反了。

总结:
当const放在右边时,限制的是指针本身(不能改变指针变量指向),可以改变指针指向内容。
当const放在
左边时,限制的是指针指向的内容,不能修改,但是可以修改指针变量本身(改变指针变量指向)
其实也好理解,*p表示指向的内容,p就是指针本身const * p限制内容, *const限制指针本身。

指针运算

指针±整数

我i们直接说结论

int a = 0;
int * p = &a;
p+1;
这里的p+1就是在指针p原来表示的地址基础上又加了4个字节,那为什么是4个字节呢,其实是个前面的int 有关的
type * p;
p+n跳过n*sizeof(type)个字节

那么这个有什么用呢,我们可以联想一下数组
在这里插入图片描述
我们可以用指针加减整数的方式访问数组元素,操作如下:

int main() {
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = &a[0];//首元素地址
	for (int i = 0; i < sz; i++) {
		printf("%d ", *p);
		p++;//每次加四个字节,正好访问下一个元素
	}
	return 0;
}

在这里插入图片描述

指针 - 指针

我们可以把指针加减数字理解为日期加减天数,得到的就是加减后的日期,那么指针减指针就是日期减日期,就是两个日期之间相差的天数,但是指针加指针是没有意义的。
在这里插入图片描述
不过指针相减前提是两个指针指向的是同一块区域,不然相减是没有意义的

指针比较大小

既然指针可以相减,那么指针肯定是有大小的,数组中随着下标的增长就是从低地址到高地址,根据这个我们来看个样例

int main() {
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = a;//数组名就是数组首元素地址
	while (p < a + sz) {
		printf("%d ", *p);
		p++;
	}
	return 0;
}

根据上面的代码同样可以实现数组的遍历。

野指针

野指针就是指针指向的位置是不可知的,随机的
在这里插入图片描述
同时呢,越界访问也会产生野指针,因为数组中内存连续,但出了数组,内存分配又是随机的

还是上面的代码,这次我们改为 i<=sz

int main() {
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = &a[0];//首元素地址
	for (int i = 0; i <= sz; i++) {
		printf("%d ", *p);
		p++;//会越界访问,变成野指针
	}
	return 0;
}

那么如何规避野指针呢
规范的进行初始化,如果不知道指针指向哪里,可以先给指针赋值NULL,使用的时候检查指针的有效性。
本次分享就先到这里了,感谢支持!

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值