通俗易懂的C语言指针知识讲解(详细,深入) 1
前言
本篇文章就让我们一起揭开C语言指针的神秘面纱,指针是C语言中特别重要的一个知识点,也是C语言的灵魂所在。可以说C语言学没学好,首先就是要看指针学没学好。本篇的学习目的是从指针的底层讲起,一步一步的不断理解指针。
指针是什么?
内存
讲指针之前让我们先了解一下内存的相关知识:
内存是计算机中非常重要的组成部分,当计算机运行程序时,它会将程序和数据加载到内存中,并在需要时从内存中获取信息。而计算机为了方便管理内存,将内存划分为一个字节大小的内存单元,并且为了方便找到这个内存单元,计算机中会给用到的内存单元,随机生成相应的编号(这个编号就相当于我们生活中的外卖地址),有了这个编号就可以快速找到内存单元,对其进行读写操作,在计算机中这个编号又被称为地址。
注:
32位的机器中有32根地址线,即一共有32bit的二进制位(4字节)
64位的机器中有64根地址线,即一共有64bit的二进制位(8字节)
1字节=8位(二进制)
1KB=1024字节
指针的说明
C语言中又给地址起了一个新的称号叫指针
由此我们可以理解为:
内存单元的编号 = 地址 = 指针。
一段关于指针的代码及其解释:
其中int表示指针指向的数据类型为int类型,*说明p是指针变量,p是指针变量,里面存的是地址。
通过调试结果我们可以看到p变量存的就是i的地址。
取地址操作符(&)和解引用操作符(*)
取地址操作符
&有两种使用情况:
(1)作为位操作符,进行按位与运算,是双目操作符 如 1&0。
(2)作为取地址操作符,取出变量名所在内存的地址,是单目操作符 如&i ,可配合%p打印出地址。
知道了取地址操作符后我们就可以查看任意变量所在内存中的地址了。比如下面这段代码就是,将i变量的地址以16进制的形式打印出来。
示例:
#include <stdio.h>
int main()
{
int i = 1;
printf("%p\n",&i);
return 0;
}
运行结果图:
这个时候我们就可以很轻松的给指针变量,进行初始化了。
示例:
int main()
{
int i = 1;
int* p = &i;//i在内存中的地址,赋值给指针变量p
printf("%p\n",&i);
printf("%p\n", p);
return 0;
}
运行结果图:
通过上面的运行结果图我们就可以知道 p里面存的就是i变量的地址。
解引用操作符
利用上面的取地址符号,我们已经成功的将i的地址成功的放到指针变量p中,那么我们该怎样去正确的访问和修改里面的这个地址里面的值呢?,这里就需要用到解引用操作符 *。
示例:
#include <stdio.h>
int main()
{
int i = 1;
int* p = &i;
printf("%p\n",&i);
printf("%p\n", p);
printf("%d\n", *p);
*p = 10;//更改i地址里面的值
printf("%d\n", *p);
return 0;
}
运行结果图:
指针变量的类型
众所周知C语言里面有很多变量类型,如:字符型,整型,浮点型…
等等。
那指针变量是不是为了存放相应变量的地址,也有相应的变量与之对应,答案是有的。
char* pc = NULL;//字符指针
short* ps = NULL;//短整型指针
int* pi = NULL;//整型指针
long* pl = NULL;//长整型指针
float* pf = NULL;//浮点型指针
double* pd = NULL;//双精度浮点型指针
..........
此外不同类型的指针变量的访问权限不同比如:
char* 字符指针的访问权限是一次能访问操作1个字节
int* 整型指针的访问权限是一次能访问操作4个字节
不同类型的指针的访问权限是根据不同类型变量在内存中所占空间的大小挂钩的。
指针运算
指针±整数
示例:
#include <stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int* p = &arr[0];//将arr[0]的地址赋值给p
char* ps = (char*)p;
printf("%p\n",p);
p=p + 1;
printf("%p\n", p);
ps=ps + 1;
printf("%p\n", ps);
return 0;
}
运行结果:
不同的指针类型加减会向后或者向前走不同的步长:
char* 指针 + 1,意思是跳过1个字符,也就是向后走1个字节
int* 指针 + 1,意思是跳过1个整形,也就是向后走4个字节
char* 指针 - 1,意思是跳过1个字符,也就是向前走1个字节
int* 指针 - 1,意思是跳过1个整形,也就是向前走4个字节
指针- 指针
指针减去指针得到是两个指针之间的元素个数
示例:
#include <stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int* p = &arr[0];//将arr[0]的地址赋值给p
int* ps = &arr[4];//将arr[4]的地址赋值给p
printf("%d\n",ps-p);
return 0;
}
ps-p得到的是0xeffbf0到0x00effc00之间的元素个数,这两个地址直接一共隔了四个元素,所以结果为4。
运行结果:
野指针
野指针就是指针指向的位置是不可知的,不确定的。
野指针的成因
- 指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机地址
*p = 10;
return 0;
}
- 指针越界访问
#include <stdio.h>
int main()
{
int arr[5] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=5; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
- 指针指向的空间释放
#include <stdio.h>
int* add(int x,int y)
{
int n = x+y;
return &n;
}
int main()
{
int*p = add(12,10);
printf("%d\n", *p);
return 0;
}
如何避免野指针
- 如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL,NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
- ⼩⼼指针越界,指针指向的范围只能在一个程序运行过程中所申请的内存范围之内。
- 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性。
- 避免返回局部变量的地址。