前言
指针可是数据结构的基础,其内容及重要又繁多,之前没有时间整理出来,现在放假了怎么说也要写一个,既是对前面的总结和梳理,也可以用来以后的查找知识,ok,废话不多,直接启动。
指针是什么
指针是C语言中的一种特殊数据类型,它存储了一个变量的内存地址。通过指针,我们可以直接访问和修改该内存地址上存储的值。指针变量本身也有自己的存储地址,而指向指针变量的存储地址就被称为指针的指针,即二级指针。多级指针的定义就是定义时使用多个"*"号。
1内存和地址
1.1内存
我们知道你的代码中的变量常量都是放在内存中的,我们电脑大多是4g或8g内存,这些内存空间被平均分了许多份,每份大小是1个字节
下面是常见的内存大小单位以及换算
我们可以把整个内存当成一个酒店,每个空间当作一个房间,那么每个房间的门牌号就是地址,我把地址告诉你了,你自然就能找到那个房间了。而在C语言中地址就是指针。
1.2地址的写法
我们知道你电脑cpu和内存要互相协同⼯作的。所谓的协同,⾄少相互之间要能够进⾏数据传递。那么如何通信呢?答案很简单,⽤"线"连起来,我们今天关⼼⼀组线,叫做地址总线。
我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么
⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含
义,每⼀种含义都代表⼀个地址。如此一来每个字节大小的空间就被编好地址了。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊
CPU内。
当然了,为了书方便我们写地址是以16进制写的,对32位机器如果某个空间地址是
10000000 10000000 10000000 10000000
换算成16进制就是0XF0 F0 F0 F0 前面的0X表示这个数是16进制,且字母大写
取地址操作符(&)
理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,⽐如:

上述的代码就是创建了整型变量a,内存中申请4个字节,⽤于存放整数10,其中每个字节都

很明显他们里面存放的值分别是0a 00 00 00,用小端换算成10进制就是10
那我们如何能得到a的地址呢? 用到操作符&

当然了这里的地址是4个地址中最小的那个,你可以理解为从这个地址开始向后4个字节都是a的空间,于是乎我们顺藤摸瓜就能找到另外三个了
那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006FFD70,这个数值有时候也是需要
2指针变量和地址
2.1取地址操作符&和指针变量
将指针存储起来,可以⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中,用什么方法呢?答案是取地址操作符
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
return 0;
}
指针变量也是⼀种变量,
这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
指针变量的类型:对应数据类型加上一个*
int对应int*
char对应char*
double对应double*
以此类推
可以理解为*是在说明他是个指针,而前面的int则说明它指向的地址里的数据是int类型,这个地址的值就被存到这个指针变量里了

2.2解引用操作符*
我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?
这里就要用到解引用操作符*了

和明显我们通过*p找到了a并且打印了出来,

这里我们通过指针p修改了a的值
可能有同学会觉得麻烦,感觉不如直接打印a或者修改a,
但以后函数的传值与传址就会明白了
2.3 指针变量的⼤⼩
前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后
是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4
个字节才能存储。
如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要
8个字节的空间,指针变的⼤⼩就是8个字节
x64环境下的

x86环境下

结论:
•
32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
•
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
•
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
3. 指针变量类型的意义
3.1 指针的解引⽤
x会将a的4个字节全部改为0,y只是将b的第⼀个字节改为0。
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如:
char*
的指针解引⽤就只能访问⼀个字节,⽽
int*
的指针的解引⽤就能访问四个字节。
3.2void* 指针
在指针类型中有⼀种特殊的类型是
void*
类型的,可以理解为⽆具体类型的指针(或者叫泛型指
针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性,
void*
类型的指针不能直接进
⾏指针的+-整数和解引⽤的运算。
int main()
{
int a = 10;
void* p = &a;
*p = 0;
p++;
return 0;
}

⼀般
void*
类型的指针是使⽤在函数参数的部分,⽤来接收不同类型数据的地址,这样的设计可以
实现泛型编程的效果。使得⼀个函数来处理多种类型的数据,
4. const修饰指针
4.1 const修饰指针变量
int main()
{
int a = 10;
const int* p = &a;
*p = 5;
return 0;
}

这时无法通过*p修改a的值


这时不能改变p的值
但是你可以通过二级指针的方法绕过去,可事实上不该这样做,因为他既然被const修饰了就说明写他的人不希望它被改变,
int main()
{
int a = 10;
int b = 5;
int* const p = &a;
int** b = &p;
int*x = &b;
b = &x;
return 0;
}
结论:const修饰指针变量的时候
•
const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
•
const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变
5. 指针运算
指针的基本运算有三种,分别是:
•
指针+- 整数
•
指针-指针
•
指针的关系运算
5.1 指针+- 整数
因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。


5.2 指针-指针

我们通过这个实现了strlen函数
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
5.3 指针的关系运算
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
6. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
6.1 野指针成因
1. 指针未初始化
# include <stdio.h>int main (){int *p; // 局部变量指针未初始化,默认为随机值*p = 20 ;return 0 ;}
2. 指针越界访问
# include <stdio.h>int main (){int arr[ 10 ] = { 0 };int *p = &arr[ 0 ];int i = 0 ;for (i= 0 ; i<= 11 ; i++){// 当指针指向的范围超出数组 arr 的范围时, p 就是野指针*(p++) = i;}return 0 ;}
3. 指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
6.2 如何规避野指针
6.2.1 指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.
NULL
是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址
会报错。
6.2.2 ⼩⼼指针越界
6.2.3 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的
时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,
同时使⽤指针之前可以判断指针是否为NULL
6.2.4 避免返回局部变量的地址
如造成野指针的第3个例⼦,不要返回局部变量的地址。
7. assert断⾔
assert.h
头⽂件定义了宏
assert()
,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报
错终⽌运⾏。这个宏常常被称为“断⾔”。
assert(p != NULL );
上⾯代码在程序运⾏到这⼀⾏语句时,验证变量
p
是否等于
NULL
。如果确实不等于
NULL
,程序
继续运⾏,否则就会终⽌运⾏,并且给出报错信息提⽰。
这个可以很好的处理野指针的问题
最后留个思考问题,函数传值与传址的区别,这就是我们为什么要学指针的一个原因。
总结
ok,今天的内容包括以下这些
1
.内存和地址
2.
指针变量和地址
3.
指针变量类型的意义
4.
const修饰指针
5.
指针运算
6.
野指针
7.
assert断⾔
也是相当多了,但关于指针我可能要写4篇博客吧,因为它真的很重要而且有难度,今天的算是把基础讲的差不多了,那么如果你觉得对你有帮助的话就点个免费的赞支持一下吧,谢谢!