本文会大量使用该博主文章的图片,因为笔记本正在维修,没办法使用一些功能用的是平板的编辑器
首先内存会划分为小的内存单元,呈线性,每一个内存单元都有一个编号,这个编号就被称为地址,我们把地址也叫指针
至于为什么内存,他好像是倒着储存的后面会讲。
注意:指针指的是地址,但是我们口语中的指针通常指的是指针变量,指针变量就是用来存放地址的变量
我们是可以通过修改指针变量的直来改变原数的值,如下:
#include
int main()
{
int a = 9;
int* p = &a;
printf("%d\n",*p);
* p = 6;
printf("%d\n",*p);
return 0;
}
a原本的值是9通过指针变量,解引用也可以改变a的值
关于指针的大小:在32位平台是4个字节大小,在64位平台是8个字节大小,并不会因为指针类型的改变而改变!:
#include
int main()
{
int a = 9;
int* p = &a;
printf("%lu\n",sizeof(p));
char i = 5;
char* pp= &i;
printf("%lu\n",sizeof(pp));
return 0;
}
观察可知,无论是整型指针还是字符指针在64位平台下内存大小都是8个字节
注意:64位平台即是64个bit组成的,所以64个bit=8个byte,因此在64位平台指针是8个字节的大小,32位也是相同的道理
指针类型决定了,指针在被解引用的时候所访问的权限,例如:
整型指针解引用会访问4个字节
字符指针解引用会访问1个字节
下面来具体证明这两个类型的指针的访问权限:
因为本人笔记本正在维修,没办法使用监视页面,所以借用博主的图,博主文章在前言
a为16进制0x11223344,在监视页面观察a与*pa相同,都是数值;&a与pa相同,都是地址
输入a的地址,发现内存中存储为44 33 22 11,这里涉及到了我们前面所讲到的大小端存储,不知道的小伙伴可以到“C语言之数据的存储”那篇博客中学习我们VS中用的是小端存储,所以说为44 33 22 11,接着运行到*pa=0后,我们再观察内存可知:
a的值被更改为了00 00 00 00,4个字节的值都被改了
同样按照刚才的顺序,如下图:
代码部分其他都不变,只变了指针的类型
监视和内存部分一样
当代码运行*pa=0后
我们发现,字符指针类型,解引用只能改变2个16进制位,即8个bit位,即1个byte上面两个例子证明了不同的指针访问权限是不同的
3、指针的类型决定向前或向后一步走了多大距离
具体意思就是说指针变量加或减一,它的地址变化的量,如下所示:
#include
int main()
{
int a = 9;
int* p = &a;
printf("%p\n",p);
char* pp= &a;
printf("%p\n",pp);
printf("%p\n",p+1);
printf("%p\n",pp+1);
return 0;
}
分别定义了整型指针变量p和字符指针变量pp, 分别用%p打印地址,观察运行结果可知:
p和pp地址相同,因为pa和pb都是指向a的地址
而p+1和pp+1则大大不同,p+1相对于p加了4,而pp+1相对于pp只加了1,从这个例子中可以看出指针的类型也决定了,指针向前或向后走一步能够走多大的距离。
————————————————
原文链接:https://blog.csdn.net/m0_64411530/article/details/125584522
野指针就是指针指向位置是不可知的
这种问题非常常见,可能是我们之前写代码的一些习惯,当时写指针的时候一定要注意:
#include
int main()
{
int* p ;
*p=10
printf("%p\n",*p);
return 0;
}
运行之后会报错:
因为代码int* p没有初始化,所以p变量中存的地址并没有指向我们当前程序的空间,而是指向内存中随机的空间,因此要*p要访问这块内存空间肯定是出错的!
这里的p就是野指针
越界访问这个词,我们在数组里面见过,也会出现在指针访问数组这个过程中。
#include
int main()
{
int arr[5]={1,2,3,4,5};
int* p = arr;
for(int i =0;i<6;i++)
{
printf("%d ",*p);
p++;
}
return 0;
}
这是因为我们设定数组arr,让指针变量p等于数组首元素的地址,数组本身有5个元素,但是在for循环中却循环6次,i分别是0 1 2 3 4 5,本身i是0~4的,循环时多了个5,导致非法访问内存,因此第六个数打印出来是随机数,这里也是野指针的问题了
test函数的返回值是x的地址,main函数中用指针变量p接收x的地址,但是x变量进入test函数创建,而出了test函数会销毁,这时再改变*p的值,即使用x的地址,则是非法访问内存了,也会造成野指针的问题
①指针要初始化
比如上方的例子中,不能直接int* p,必须要初始化,int a = 10;int* p = &a;从而规避野指针问题
②要注意指针越界的问题
在我们使用数组时,一定要注意数组的元素个数以及我们所循环的次数,避免粗心而导致越界访问
③指针所指向的空间及时置NULL
在我们不使用指针变量p时,int* p = NULL;置为空,在接下来想要使用p时,用if语句:
if(p != NULL) …
能够很好地避免野指针
④避免返回局部变量的地址
就像上方野指针成因的第三条所举的例子,避免返回局部变量的地址
⑤指针使用之前检查有效性
像if(p != NULL) …就是在检查指针的有效性
————————————————
原文链接:https://blog.csdn.net/m0_64411530/article/details/125584522
首先大家可以先了解一下const这个关键字
const int n=5;
int const n=5;
这两种写法是一样的,都是表示变量n的值不能被改变了,需要注意的是,用const修饰变量时,一定要给变脸初始化,否则之后就不能再进行赋值了。
常量指针说明本质他是一个指针,常量是指针指向的内容。我们知道常量他是不能变的,所以说他指向的内容是不能变的,但是指针指向的地址可以变的
#include
int main()
{
int a = 10, b = 20;
const int *p = &a;
printf("%d ",*p);
p = &b;
printf("%d ",*p);
return 0;
}
注意:a,b的值是不能被修改的
指针常量也是一样的道理,他本质上是一个常量,是一个指针类型的常量,指针具有常量的特征,指针不能改变,也就是地址不能改变,但是指针指向的值是可以改变的
#include
int main()
{
int a = 10;
int *const p = &a;
printf("%d ",*p);
*p = 20;
printf("%d ",*p);
return 0;
}
注意:因为指针常量本质上是常量,所以在定义的时候是可以这样的
#include
int main()
{
int m = 10;
const int * ptr7; // 正确
ptr7 = &m; // 正确
printf("%d ",*ptr7);
return 0;
}
注意:常量地址不能初始化普通指针吗,常量地址只能赋值给常量指针
int main() {
int m = 10;
const int n = 20; // 必须在定义的同时初始化
const int *ptr1 = &m; // 指针指向的内容不可改变
int * const ptr2 = &m; // 指针不可以指向其他的地方
ptr1 = &n; // 正确
ptr2 = &n; // 错误,ptr2不能指向其他地方
*ptr1 = 3; // 错误,ptr1不能改变指针内容
*ptr2 = 4; // 正确
int *ptr3 = &n; // 错误,常量地址不能初始化普通指针吗,常量地址只能赋值给常量指针
const int * ptr4 = &n; // 正确,常量地址初始化常量指针
int * const ptr5; // 错误,指针常量定义时必须初始化
ptr5 = &m; // 错误,指针常量不能在定义后赋值
const int * const ptr6 = &m; // 指向“常量”的指针常量,具有常量指针和指针常量的特点,指针内容不能改变,也不能指向其他地方,定义同时要进行初始化
*ptr6 = 5; // 错误,不能改变指针内容
ptr6 = &n; // 错误,不能指向其他地方
const int * ptr7; // 正确
ptr7 = &m; // 正确
int * const ptr8 = &n;
*ptr8 = 8;
return 0;
}