通过指针可以间接的访问内存,假设你在一个大型的程序中,编写了很多函数,这些函数都需要同时访问,修改一块内存空间上的数据,而指针可以让这些函数共享同一块内存地址,从而指针像一个真正意义上的 “针” 一样,将我们的程序连接起来,实现对数据高效的共享。
题外话:比如有玩英雄联盟/王者荣耀的,你可以把整个峡谷当作一个内存空间,而你的屏幕就可以当作一个指针,这个指针指向内存空间的一小块,就是你的视角对应整个峡谷的视角,你在拉视野就等于你在修改指针的指向。打游戏要多看小地图,多拉视野,多支援队友啊喂。没有指针的话你就拉不动视野了,想想指针有必要不。
指针类型 和
int类型
,double类型
一样,也是一种数据类型,但是指针是一种依附于其他数据类型的一种数据类型。内存地址都是有编号的,从0开始记录(题外话:在大多数操作系统上面,程序不允许访问地址为 0 ~ 255的内存,因为该内存是为了操作系统所保留的)一般的内存地址用16进制的编号表示,比如0x00000010;
在使用指针的时候我们会频繁的进行以下几个操作:
- 定义一个指针变量,把普通变量的地址赋值给指针变量
- 访问指针变量存放的内存地址上存放的变量
通过下面两个运算符我们可以进行这些操作
int a = 10;//创建一个普通的整型变量
int* p;//创建整型的指针变量 这里的 * 号表示 p 是一个指针变量
p = &a;//将普通的整型变量的在内存上的地址赋值给指针变量
*p = 20;//这里的 * 号是解引用操作,主要用来操作 p 存放的内存地址上存放的数
#include
int main()
{
print("sizeof char* = ",sizeof(char*));
print("sizeof short* = ",sizeof(short*));
print("sizeof int* = ",sizeof(int*));
print("sizeof double* = ",sizeof(double*));//32位操作系统下:都打印 4
return 0;
}
在32位操作系统下,指针的大小都是四个字节,因为指针本身就是一种用来存放地址编号的数据类型,存放地址编号四个字节就行,而与指针指向的数据类型是什么没有关系。
- 空指针就是定义一个指针变量,指向地址编号为 0 的内存空间
int *p;
p = NULL;//p = nullptr;
- 不可以访问空指针指向的内容
- 空指针的主要用途是用于定义指针变量时的初始化
- 指向非法的内存空间的指针
- 未初始化的指针
int *p;
p = 0xffff;//0xffff这块地址空间从未申请
printf("%d\n",*p);
没有申请0xffff
这块内存,但是我们直接定义一个指针变量指向这块内存,输出这块内存的内容,这是不合法的,指针p就叫野指针,同样,未初始化的指针指向那块内存空间是未知的,也是违法的,程序运行起来会崩溃;
- 修饰函数的返回值类型
- 修饰函数的形参
!!!void 不能创建变量
#include
int main()
{
void* p1;
void* p2;
int a = 10;
double b = 20;
p1 = &a;
p2 = &b;
printf("%d",*(int*)p1);//void* 不可以直接解引用,需要强制转换之后才能使用
printf("%lf",*(double*)p2);
return 0;
}
例:
int* p1 = NULL;
char* p2 = NULL;
p1 = p2;//到这里会出现错误,需要把p2 强制转换成int*
void* p3;
p3 = p1;//这里不会出错,不需要强制转换
void*
可以保留任意数据类型的指针的地址编号,并且在使用的时候可以任意转换成我们想转换成的数据类型
const
修饰的指针const
的情况int a = 10;
int *p;
p = &a;// p 指向 a
*p = 20; //a 可以改变
int b = 30;
p = &b;// p 指向 b
没有const
限制的情况下,指针的指向可以改变,同时, 指针指向的数据也也可以改变;
总结:这是一个海王遇到了一个海女,两个都不专心的情况,最后海王又找了一个海女
const
修饰 * 的情况int a = 10;
int const *p = &a;//也可写为const int *p = &a;
*p = 20;//报错
int b = 20;
p = &b;//正确
当
const
修饰 *p 的时候,*p是不可以更改的,但是p的指向是可以修改的
总结:这是一个海王祸害好女孩的情况
const
修饰 p 的情况int a = 10;
int * const p = &a;
*p = 20;//正确
int b = 20;
p = &b;//错误
当
const
修饰 p 的时,p 指向的数据(*p)可以更改,但是 p 的指向不可以更改
总结:这是一个老实人爱上海女的情况
const
修饰 * 和 p 的情况int a = 10;
int const * const p = &a;
*p = 20;//错误
int b = 20;
p = &b;//错误
当
const
修饰 * 和 p 的时候,指针指向的值和指针的指向都不可以改变
总结:这是真爱的情况
先上图:(当初老师教的时候放过这张图[doge])
指针可以加上或者减去一个整数,但是这种指针的数值上面的加减和普通数值上的加减是不一样的。
可以设计一个程序测试:
#include
int main()
{
char* p1 = NULL;//char 类型为一个字节
int* p2 = NULL;//int 为四个字节
for (int i = 0; i < 5; i++)
{
printf("p1 = %d ",p1);
printf("p2 = %d\t\n",p2);
p1++;
p2++;
}
return 0;
}
最终的输出结果如下:
p1 = 0 p2 = 0
p1 = 1 p2 = 4
p1 = 2 p2 = 8
p1 = 3 p2 = 12
p1 = 4 p2 = 16
在程序中,我们给
p1
和p2
赋值,同时指向内存中的第0个字节,然后打印指针的值,同样是加一的操作,可以看出,指向char
类型的指针p1
每次加一,指针的值就加上了sizeof(char)
,指向int
类型的指针每次加一,指针的值就加上了sizeof(int)
;通过上面的测试,其实你自己也可以设计一个程序,看看short型
(2字节) 和double型
(8字节),看看是不是指针每次加一,对应的指针的值会加上指针指向的数据类型的字长;同样的,减法操作和加法操作一样,指针的值也会减去指针指向的数据类型的字长;
先看下面的代码:
int num = 0x01020304;//16进制的数字一个数字占 4bits,两个数字就是一个字节
int *p = #
short *p2 = #
有一个
int型
指针和一个short型
指针,分别指向了num
这个16进制的数字,以下为数字在内存中的存储方式(数据在内存中的大端小端存储方式有兴趣自行百度-)
int型
的指针在解引用的时候可以把内存中的首地址字节加上后面连着的三个字节给解出来,总共四个字节,而short型
的指针只能将首地址字节加上后面一个字节解出来,总共两个字节。
所以设计程序如下:
#include
int main()
{
int num = 0x01020304;
int* p = (short*)#
short* p2 = (char*)#
printf("*p = %#x\n", *p);
printf("*p2 = %#x", *p2);
return 0;
}
程序的输出为:
*p = 0x1020304
*p2 = 0x304
开头的0被省略了,实际上就是*p = 0x01020304,*p2 = 0x0304
抱着学习的态度,这个时候我们把
p2 + 1
,那么是不是可以把前面的0x0102解出来了呢?说干就干
#include
int main()
{
int num = 0x01020304;
int* p = #
short* p2 = (short*)#
printf("*p = %#x\n", *p);
printf("*p2 = %#x\n", *p2);
p2++;
printf("*p2 = %#x", *p2);
return 0;
}
程序输出结果为:
*p = 0x1020304
*p2 = 0x304
*p2 = 0x102
对应了指针加一,指针的值就会加上指针所值的数据类型的字节大小的情况。