目录
1、指针是什么?
2、指针和指针类型
2.1、指针+ - 整数
2.2、指针的解应用
3、野指针
3.1、野指针成因
3.2、如何规避野指针
4、指针运算
4.1、指针加整数
4.2、指针-指针
4.3、指针的关系运算
5、指针和数组
6、二级指针
7、指针数组
在写C语言程序的时候,创建的变量、数组都需要在内存上开辟空间。每个内存都
有唯一的编号,这个编号也称为地址编号,也就是地址,即指针 ;如下图
地址编号 = 地址 = 指针
这里我们需要明确两个要点:
1、指针是内存中最小单元的编号,也就是地址,这上面我们已经解释过了
2、平时口语说的指针,通常是指针变量,是用来存放内存地址的变量,这里做何解释呢?
int a = 0;
int* p = &a;
//&a取出a的地址
比如上述代码,p就是我们口语所说的指针,但其实它是一个指针变量,里面存放的是a的地址
其实看过博主前面的文章的宝子都知道,博主在前面讲过指针变量的大小为4个字节和8个字节。当时没有做出解释,其实这里就需要涉及到知道计算机当中是如何编址的?
那我们这里总共就有2^32个地址,每一个地址标识一个字节,那么就有4GB的空闲进行编址,计算如下
那么对于64位的机器来说,我们就会有2^64个地址,每个地址标识一个字节,而对于一般的计算机而言我们不会把64根线全用上,通常用8GB的空闲进行编程,计算如下
而我们的指针变量存放着我们的地址,所以我们就明白了
1、地址就是指针,口语中的指针指的是指针变量
2、在32位的机器上,指针变量的大小为4;在64为的机器上时,指针变量大小为8
指针的类型和定义方式
int * | 存放int类型变量的指针 |
char * | 存放char类型变量的指针 |
short * | 存放short类型的变量 |
long * | 存放long类型的变量 |
float * | 存放float类型的变量 |
double * | 存放double类型的变量 |
指针的定义方式 | type + * |
这里我们需要明确,指针就是地址,所以指针 + - 整数,其实就是对地址+ -;而指针类型决定着+/-1操作时所需要跳过的字节数;比如整形指针+1就跳过4个字节;char类型指针+1就跳过一个字节,应用如下:
如上图,pa为整型,pa+1后地址从00BBFA38-->00BBFA3C,跳了四个字节;pc为char类型,pc+1后地址从00BBFA38-->00BBFA39,跳了一个字节;博主这里只移动了一个字节,若移动n个,只需要用指针所在类型所占字节乘以n就行
type * p //*说明p是指针变量 |
1.p指向的对象的类型 |
2.p解引用的时候访问的对象大小是sizeof(type) |
首先我们先看一段代码的运行结果
这里我们发现,我们用解引用操作符访问int型的pa时,这时候四个字节的数都变为了0;
那么这时候我们再看一段代码:
而这里,我们用解引用操作符访问char型的pa时,这个时候只有一个字节的数变为了0;a变为了0x11223300,这里需要注意0x11223344在存储时是倒着存储的,由于这里a为16进制数,一位16进制的数需要用4个2进制数表示,也就是4比特位,两位就是8比特位为一个字节,所以题中用解应用操作符访问char型的pa时,44都变为了0,就是因为44占用一个字节;
总结
指针类型可以决定指针解引用的时候访问多少个字节(指针的权限)
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
1、指针未初始化
#include
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2、指针越界访问
#include
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3、指针指向的空间释放
//int* test()
//{
// int a = 110;
// return &a;
//}
//
//int main()
//{
// int* p = test();
// printf("%d\n", *p);
//
// return 0;
//}
上述代码在调用test()函数时,这时候内存会为a变量开辟一块空房间,这时候&a会取出a的地址并返回,但是这时候由于test()函数的结束,a的空间还给操作系统了,那么这里指针p指向空间就会被释放,p就变成了野指针;
1.指针初始化:明确知道指针初始化为谁的地址,就直接初始化;不知道指针初始化什么值,暂时初始化为NULL;
2.小心指针越界
3.指针指向空间释放,即使置NULL
4.避免返回局部变量的地址
5.指针使用之前检查有效性
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
对指针加1其实是加上指针所指向类型的大小,指针的类型决定了指针向前或者向后走一步有多大(距离)。
这里需要注意:
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
//int main()
//{
// int arr[10] = { 0 };
// printf("%d\n", &arr[9] - &arr[0]);//9
// printf("%d\n", &arr[0] - &arr[9]);//-9
//
// return 0;
//}
指针 - 指针得到的数值的绝对值:是指针和指针之间的元素个数
而这项运算的前提条件是:指针和指针指向了同一块空间
地址是有大小的,指针的关系运算就是比较指针的大小
#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
如上述代码就是一个简单的指针的关系运算,对velaus进行初始化
在将指针和数组之前,我们得清楚指针和数组的关系,这里我们需要明白
指针变量就是指针变量,不是数组,指针变量的大小是4/8个字节,专门用来存放地址的。数组就是数组,不是指针,数组是一块怜恤得控件,可以村放一个或者多个类型相同得数据。
既然我们我们讲得是指针和数组,那么它们之间有什么关系呢?哲理我们还需要明白
数组中,数组名其实是数组首元素的地址,数组名==地址==指针,当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组可以通过指针来访问
如果还不明白他们之间的关系,可以看一看下图
这里我们需要注意:数组名是首元素地址
两个情况意外:sizeof()和&
二级指针就是用来存储指针变量的变量,怎么理解呢?我们先看一段代码;
int main()
{
int a = 10;
int* p = &a;
return 0;
}
如上述代码,p是指针变量,用来存放a的地址,但是p是变量,只要是变量就需要在内存中开辟空间,而二级指针里面存放就是p的地址;
我们监视后发现确实如此
使用格式如下
int** pp = &p;
//int*是在说明pp指向的p是int*类型的变量
//*在说明pp是一个指针变量
我们再来看一下简单应用
//int main()
//{
// int a = 10;
// int* p = &a;//p是一级指针变量,指针变量也是变量,变量是在内存中开辟空间的,是变量就有地址
// int** pp = &p;//pp就是二级指针变量,二级指针变量就是用来存放一级指针变量的地址
// **pp = 100;
// printf("%d\n", a);//结果为100;
// return 0;
//}
指针数组顾名思义就是用来存放指针(地址)的数组,下面就是一个简单指针数组,里面存放的是指针(地址)
若想使用里面的数据,使用方法如下,我们需要先在指针数组选择,访问那一地址,然后我们据根据需要选择所选地址里的内容,逻辑如下:
知道了逻辑,我们可以简单应用实现一下
这里有个误区需要注意,虽然指针数组访问方式与二位数组相同,但他们是不一样的,二维数组在内存中是连续存储的,指针数组不一定,它是将一个个地址集合放在了一个指针数组里面,就像拼装在了一起
创作不易,一件三连加关注!!!
创作不易,一件三连加关注!!!