再识C语言 DAY14 【指针(上)】

文章目录

  • 前言
  • 什么是指针
  • 指针类型
    • 1、整型指针的访问权限说明:
    • 2、字符指针的访问权限说明:
  • 野指针
    • 野指针的成因
      • 1、指针未初始化
    • 2、指针的越界访问
    • 3、指针所指向的空间释放了
    • 如何避免野指针
  • 常量指针和指针常量
    • 1、常量指针——指向“常量”的指针(const int *p, int const *p)
    • 2、指针常量——指针类型的常量(int *const p)
    • 例题
  • 如果您发现文章有错误请与我留言,感谢


前言

本文会大量使用该博主文章的图片,因为笔记本正在维修,没办法使用一些功能用的是平板的编辑器


什么是指针

首先内存会划分为小的内存单元,呈线性,每一个内存单元都有一个编号,这个编号就被称为地址,我们把地址也叫指针

再识C语言 DAY14 【指针(上)】_第1张图片

至于为什么内存,他好像是倒着储存的后面会讲。

注意:指针指的是地址,但是我们口语中的指针通常指的是指针变量,指针变量就是用来存放地址的变量
我们是可以通过修改指针变量的直来改变原数的值,如下:

#include
int main()
{
	int a = 9;
int* p = &a;
printf("%d\n",*p);
* p = 6;
printf("%d\n",*p);
	return 0;
}

再识C语言 DAY14 【指针(上)】_第2张图片

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;
}

再识C语言 DAY14 【指针(上)】_第3张图片

观察可知,无论是整型指针还是字符指针在64位平台下内存大小都是8个字节
注意:64位平台即是64个bit组成的,所以64个bit=8个byte,因此在64位平台指针是8个字节的大小,32位也是相同的道理

指针类型

指针类型决定了,指针在被解引用的时候所访问的权限,例如:
整型指针解引用会访问4个字节
字符指针解引用会访问1个字节
下面来具体证明这两个类型的指针的访问权限:

因为本人笔记本正在维修,没办法使用监视页面,所以借用博主的图,博主文章在前言

1、整型指针的访问权限说明:

再识C语言 DAY14 【指针(上)】_第4张图片

a为16进制0x11223344,在监视页面观察a与*pa相同,都是数值;&a与pa相同,都是地址

再识C语言 DAY14 【指针(上)】_第5张图片
观察此时内存中a储存的值

再识C语言 DAY14 【指针(上)】_第6张图片
输入a的地址,发现内存中存储为44 33 22 11,这里涉及到了我们前面所讲到的大小端存储,不知道的小伙伴可以到“C语言之数据的存储”那篇博客中学习我们VS中用的是小端存储,所以说为44 33 22 11,接着运行到*pa=0后,我们再观察内存可知:

再识C语言 DAY14 【指针(上)】_第7张图片
a的值被更改为了00 00 00 00,4个字节的值都被改了

2、字符指针的访问权限说明:

同样按照刚才的顺序,如下图:
代码部分其他都不变,只变了指针的类型

再识C语言 DAY14 【指针(上)】_第8张图片
监视和内存部分一样
再识C语言 DAY14 【指针(上)】_第9张图片
当代码运行*pa=0后
再识C语言 DAY14 【指针(上)】_第10张图片
我们发现,字符指针类型,解引用只能改变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;
}

再识C语言 DAY14 【指针(上)】_第11张图片

分别定义了整型指针变量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

野指针

野指针就是指针指向位置是不可知的

野指针的成因

1、指针未初始化

这种问题非常常见,可能是我们之前写代码的一些习惯,当时写指针的时候一定要注意:

#include
int main()
{
int* p ;
  *p=10
printf("%p\n",*p);
	return 0;
}

运行之后会报错:

请添加图片描述

因为代码int* p没有初始化,所以p变量中存的地址并没有指向我们当前程序的空间,而是指向内存中随机的空间,因此要*p要访问这块内存空间肯定是出错的!

这里的p就是野指针

2、指针的越界访问

越界访问这个词,我们在数组里面见过,也会出现在指针访问数组这个过程中。

#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;
}

我们发现出现了一个我们数组里没有的数字
再识C语言 DAY14 【指针(上)】_第12张图片

这是因为我们设定数组arr,让指针变量p等于数组首元素的地址数组本身有5个元素,但是在for循环中却循环6次,i分别是0 1 2 3 4 5,本身i是0~4的,循环时多了个5导致非法访问内存,因此第六个数打印出来是随机数,这里也是野指针的问题了

3、指针所指向的空间释放了

这和我们所说的局部变量很像,离开了作用域就被销毁
再识C语言 DAY14 【指针(上)】_第13张图片
这里我的平板直接报错了,卡了很久

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修饰变量时,一定要给变脸初始化,否则之后就不能再进行赋值了。

1、常量指针——指向“常量”的指针(const int *p, int const *p)

常量指针说明本质他是一个指针,常量是指针指向的内容。我们知道常量他是不能变的,所以说他指向的内容是不能变的,但是指针指向的地址可以变的

#include 
int main()
{
  int a = 10, b = 20;
const int *p = &a;
printf("%d ",*p);
p = &b;
printf("%d ",*p);
	return 0;
}

再识C语言 DAY14 【指针(上)】_第14张图片

注意:a,b的值是不能被修改的

2、指针常量——指针类型的常量(int *const p)

指针常量也是一样的道理,他本质上是一个常量,是一个指针类型的常量,指针具有常量的特征,指针不能改变,也就是地址不能改变,但是指针指向的值是可以改变的

#include 
int main()
{
  int a = 10;
int *const p = &a;
printf("%d ",*p);
*p = 20;
printf("%d ",*p);
	return 0;
}

再识C语言 DAY14 【指针(上)】_第15张图片

注意:因为指针常量本质上是常量,所以在定义的时候是可以这样的

#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;
}

如果您发现文章有错误请与我留言,感谢

你可能感兴趣的:(c语言,开发语言,学习)