目录
一:指针基本知识
1.指针的概念(什么是指针)
指针简单地来说就是地址。在内存中储存的每一个数据都有属于自己的地址。而为了保存一个数据在内存中的地址,我们就需要指针变量。
因此:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。
但,为什么储存数据需要有地址呢?
这就需要我们从操作系统的角度来看这个问题。在程序员的眼中,用来存放数据的内存条应该是如下的。
可以说内存是一个线性排列且递增的“平面地址”,其每一个字节的大小都是固定的,都是由8个2进制位组成。需要我们注意的是,每一个字节都有属于自己唯一的编号,且这个编号是从0开始的。上图是一个256M的内存,它一共有256x1024x1024=268435455个字节大小。相应的地址的取值范围也就是0~268435455.
2.指针的类型
在我们之前学习C语言数组时曾了解过,数组有分为int类型数组,double类型数组,char类型数组;而对我们指针来说同样也分为int指针类型,double指针类型,char指针类型。
3.指针的作用(用指针来做什么)
1.需要传入较大的数据时作参数。
2.传入数组后对数组作操作。
3.函数返回不止一个结果。
4.需要用函数来修改不止一个变量。
5.动态申请的内存。
二:野指针
1.野指针产生的原因
指针未被初始化
指针越界访问
指针指向的空间释放
前面两点比较好理解,我们来着重看一下第三点是什么意思
include<stdio.h>
int* test( )
{
int a = 5;
return &a;
}
int main()
{
int* p = test();//将a地址传给*p
*p = 10;
return 0;
}
上段代码中我们自己定义了一个test()函数,并在test()函数中对a进行赋值,然后在main()将a的地址传给p,但是,我们应该知道在test()函数中定义的变量出了该函数之后便会“报废”,所以出了test()函数后a的地址释放,导致p变成了野指针。
2.如何避免产生野指针
- 小心越界。
- 及时把指针赋成空指针。
- 避免返回局部变量的地址。
- 使用指针前检查有效性。
三:指针与数组
1.指针数组
定义:存放指针的数组(int* arr[])。我们知道有整型类型的数组int arr[],还有字符类型的数组char arr[],指针数组就是指针类型的数组
include<stdio.h>
int main()
{
int a = 0;
int b = 1;
int* p1 = &a;
int* p2 = &b;
int* arr[] = { p1,p2 };//指针数组
int* arr[] = { &a,&b };//指针数组
return 0;
}
上图中的数组中存放的每个元素都是指针。
2.数组指针
定义: 指向数组的指针int (*)[]。
int (*arr)[10]={0,1,2,3,4,5,6,7,8,9}
其实要区别指针数组和数组指针也是有方法的,那就是看“*“与谁结合。
比如
int* arr1[5]={0,1,2,3,4}//此处星号“*”先与int结合,表明该数组中的所有元素都是int型的指针,那这便是指针数组
int (*arr2)[5] ={0,1,2,3,4}//此处星号“*”先与“arr2“结合,说明该数组一整个都是指针,这便是数组指针。
四:指针运算
在讲指针运算之前,我们先来看一段代码
include<stdio.h>
int main()
{
char ac[]={0,1,2,3,4,5,6,7,8,9};
*p = ac;
printf("p=%p",p);
printf("p+1=%p",p+1);
int ai[]={0,1,2,3,4,5,6,7,8,9};
*q = ai;
printf("q =%p",q);
printf("q+1 =%p",q+1);
return 0;
}
输出结果如下
p=0xbffbad5a
p+1=0xbffbad5b
q=0xbffbad2c
q+1=0xbffbad30
p和q都同时加上一个1,但为什么最终出来的结果不想同呢?
在此之前我们需要知道C语言中sizeof(int)= 4,而sizeof(char)= 1.
并且在16进制中“c”代表12,而“0”则代表满了16往前面进了一位。0x2c=44,0x30=48. 48-44=4.
很巧的是sizeof(int)的大小也为4.
同样的,0x5a=91,0x5b=92. 92-91=1.恰好也是一个sizeof(char)的大小。其实在这里我们可以看出一点,在指针的运算中当我们给一个指针加一时,他并不是给指针所对应的地址加一,而是加上一个sizeof(int/char/double)的大小。
这里需要借助上文的图来理解一下了
地址的储存是以字节为单位的,int型的大小为4个字节,而char型的大小为1个字节单位。
所以在指针加减运算时不能单纯地对其地址进行相应的加减,需要对应该类型的字节大小在其地址上相加减。
可能也许会有人纠结,到底能不能对地址进行加减呢?其实呢这个问题是没有意义的。举一个很简单的例子吧
拿int型来说,一个int型是4个字节大小,如果对其地址加一,但是其始终是有四个字节大小,这一点是不会改变的,在加一之后,该单元的3个字节难道会与后面一个单元的第一个字节再组成一个新的单位的int吗?这显然是不合理且无意义的。所以咱也不必过于纠结这一点。同样的对指针进行乘除也是没有意义的。
补充:0地址
0地址在我们内存中当然也是存在的,只不过我们不能接触到,所以在我们编写代码时0地址是不能出现在我们的代码当中的。
那么0地址是怎么一回事儿呢?要知道在我们所有的程序运行时,我们的操作系统都会给它一个从0地址开始的虚拟的地址空间,也就是说所有的程序在运行时都以为自己有一片从0开始的连续的地址空间,至于这空间的顶值是多少呢。如果是32位的机器的话,这个顶值就是4个g。
如果在写代码过程中需要用到0地址,NULL便是我们可以用来表示0地址的一个东西。