指针专题-----------再探指针(二)

本文详细解析了C语言中指针的四大核心概念:指针类型、指向类型、指针值及内存空间,探讨了指针算术运算的规则与实践,并强调了指针安全性的关键问题。

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

一、指针的那些事

    说起指针,关于指针的四个方面一定要清楚:

  1. 指针的类型
  2. 指针指向的类型
  3. 指针的值
  4. 指针本身所占的内存空间

1、指针的类型

<1>  int* p;
<2>  int** p;
<3>  char* p;
<4>  double* p[3];

无论上面定义的指针类型你是否认识,有一个判断指针的万能公式----把指针名去掉,剩下的就是是指针的类型

比如int** p;  这个指针自身的类型就是 int**  (去掉指针名p).。

2、指针指向的类型

这个和上面的判断方法类似,判断一个指针的指向类型时,把指针名和前面的一个*去掉,剩下的就是指针指向的类型

比如 int** p;   这个指针指向的类型就是int * 。

作用:决定了指针变量所取空间的宽度,决定了指针变量+1时跳过的单位跨度(★★★)

关于上面的作用可能不是很好理解,等到下面讲到指针的运算符会详细的解释上面的作用

3、指针的值

在定义指针会申请一块内存空间在存放这个指针,这个内存空间里面村的就是指针的值,通过指针的值一般都是第一个地址,这个地址都是指针所指向的的地址。定义指针时,仅仅是申请了一块内存空间来存放指针的地址,但是这个内存空间并没有初始化。

4、指针本身所占的内存空间

在定义指针时,肯定要去内存空间中申请一块内存空间来存放这个指针。

一开始发现了一个特别有趣的事:32位的系统中,无论什么类型的指针,都是用4个字节进行存储,64位的系统都是用8个字节进行存储。

#include<iostream>
using namespace std;

int main()
{
	cout << sizeof(int **) << endl;
	cout << sizeof(char *) << endl;
	cout << sizeof(double *) << endl;
	cout << sizeof(long long int*) << endl;
	return 0;
}

上面的代码在32位的系统中运行,输出的都是4,在64位的系统中运行,输出的都是8。

一开始特别困惑,不同的指针类型,它们怎么可能占用的内存空间一样呢?

最后去网上搜了搜,又查了一些书,最后得到了一个说服自己的理由:

      假如是32的系统,那么内存空间的地址编号长度肯定都是一样长的,而指针的值存放的就是一个个的地址,那么不同类型指针占用的内存空间可以是一样大的,因为他们存放的地址长度都是一样的。

总结:一旦遇到了一个指针,自己就应该问问自己?指针的类型是什么?指针指向的类型是什么?指针的值是什么?

二、指针的算术运算

1、关于整形数的存储

首先看一张图片

假如我们定义了一个整型变量,又赋予了一个它一个初值(16进制的11223344).那他在系统中的存储方式如上图所示(采取的是小端存储,至于是大端还是小端存储,这个和操作系统有关,window的系统是小端存储)。

变量a的首地址都是地址1。当使用到变量a是,他就会拿着a的地址去内存中找,从这个地址一次往后找四个字节(找几个节和这个变量是什么类型的有关)。然后根据这四个字节读出来一个整数(至于怎样读的,就不是咱们考虑的,操作系统既然能存进去,它肯定也可以读出来)。

2、关于指针的运算

首先看  代码1

#include<iostream>
using namespace std;

int main()
{
	int a = 0x11223344;
	printf("%#x\n", a);//按16进制数字进行输出

	int* p1 = &a;
	printf("%#x\n", *p1);//按16进制数字进行输出

	short* p2 = (short*)&a;
	printf("%#x\n", *p2);//按16进制数字进行输出

	char* p3 = (char*)&a;
	printf("%#x\n", *p3);//按16进制数字进行输出
	return 0;
}

代码1运行结果:

根据整数的存储,那么指针的访问如下图所示

下面再看 代码2

#include<iostream>
using namespace std;

int main()
{
	int a = 0x11223344;
	printf("%#x\n", a);//按16进制数字进行输出

	int* p1 = &a;
	printf("%#x\n", *p1);//按16进制数字进行输出

	short* p2 = (short*)&a;
	p2++;
	printf("%#x\n", *p2);//按16进制数字进行输出

	char* p3 = (char*)&a;
	p3++;
	printf("%#x\n", *p3);//按16进制数字进行输出
	return 0;
}

代码2 运行结果:

经过上面的代码后,指针p2和p3指向的位置都变了,下面通过一张图片看他们移动后的指向

先说指针p1,他的指向是地址1,指针指向的类型是int(4个字节),所以说指针p1的指向地址是地址1,读取的宽度是4个字节,因此*p1的值就是0x11223344(因为采取的小端存储方式,倒着存倒着取)。

再说指针p2,这个指针一开始的指向的地址1,指针指向的类型是short ( 2个字节 ) ,指针读取的宽度2个字节,因此指针p2移动前,*p2读取的数据就是0x3344。采取p2++后,指针p2向后移动一个单位长度(单位长度就是2个字节,这个和指针指向的类型有关)。那么首地址都变成了地址3,读取宽度是2个字节,因此读取的数值就是0x1122

最后说指针p3.这个指针一开始指向的地址1,读取宽度是1,因此*p3读取的数值就是0x44。 

p3++后,向后移动一个单位长度(一个字节),读取长度是1,因此 *p3读取的数值就是0x33.

三、指针的安全行问题

1、空指针

在定义指针时,我们一般都要给指针赋初值,如果还没有地址给这个指针赋初值,可以赋给指针一个NULL。

上面是一个特别好的习惯,不过有一点需要注意的就是空指针不能别访问,

原因:NULL其实就是内存空间的0号地址,操作系统中0-255的地址空间都不能被访问,这个地址只能由操作系统进行管理,程序员不可以进行修改的读取。

2、野指针

假设我们现在定义了一个指针,同时由赋给了它一个地址(直接进行指定的常量地址),那这个指针就称为野指针。

#include<iostream>
using namespace std;

int main()
{
	int* p = &(0x01020304);
	cout << *p << endl;
	return 0;
}

就像上面那样,直接指定了一个地址赋给了指针,这就是野指针,我们在使用指针时千万不要这样做。

就像你去酒店开房一样,只有你去前台找管理的人,让他给你选择一个空的房间,这样你才能入住,假如少了前面那一步,你可以直接找一个房间进去吗,说不定里面就有人在干啥呢!!!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值