一.什么是指针&&为什么需要指针?
1.什么是指针?
指针是内存中最小内存单元编号,也就是地址
平时口语上说的指针是指指针变量,用于存放地址的变量
2.为什么需要指针?
1.首先,为了管理我们计算机内存,把内存分为很多个小的内存单元,每个内存单元是一个字节;
2.为了方便使用这些内存单元,又把每个内存单元都给一个唯一的编号(下面会讲解编号怎么来的),这个编号也称为地址;在C语言中,这个地址也叫指针,所以编号==地址==指针;
当我们在写c语言程序时,创建的变量,数组等都会在内存中开辟空间;
以一个代码为例观察地址:(这里我们可以通过调试和打印两种方式观察)
1.调试观察:
这里的操作符是 “&”----->是取地址操作符 ,是个单目操作符
&a 的作用是 把a的地址取出来
2.打印观察(地址是用%p 打印):
在同一次的程序运行时,我们可以观察到,两种方式观察到的地址是一样的,但是如果我们再次运行时,地址就会发生变化,(如下图:)
这是因为在每次程序运行时,电脑会随机分配地址(这里也不是完全随机,也会考虑一些因素:如该地址是否被占用等因素),所以同一个代码,在每次运行时所分配的地址可能都会又差异;
二.地址编号的来源&&指针变量的大小
产生来源
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0); 那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
一共就有2^32个地址
如果是64位机器上就是2^64个地址
指针变量大小
在32位机器上,一个地址是32位二进制序列,即4个字节大小
在64位机器上,一个地址是64位二进制序列,即8个字节大小
注意:指针变量大小即(4 / 8个字节)只与是多少位机器有关,与变量所指向的内容无关
如:在32位机器上
int * p ---------> p的大小是4个字节
char * p1 --------> p1的大小是4个字节
short * p2 -------> p2的大小是4个字节
代码验证:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { printf("%d\n",sizeof(int *)); printf("%d\n", sizeof(char*)); printf("%d\n",sizeof(short *)); return 0; }
结果:
三.指针类型
1.指针都有哪些类型
类型(就是去掉变量名字,剩余的就是类型)
char* p ------> char*
short* p ------> short*
int * p ------> int*
long* p ------> long*
float* p ------> float*
double* p ------> double*
等等.......
这里可以看到,指针的定义方式是: type + *
其实: char* 类型的指针是为了存放 char 类型变量的地址;
short* 类型的指针是为了存放 short 类型变量的地址;
int* 类型的指针是为了存放 int 类型变量的地址;
2.指针类型的意义
1.指针类型决定了指针在解引用时,可以访问多少个字节(即指针的权限)
讲解的代码:
分析:
从上面的例子我们可以看出:int * 的指针解引用可以访问4个字节
char *的指针解引用可以访问1个字节
我们也可以推出: short * 的指针解引用可以访问2个字节
long long * 的指针解引用可以访问8个字节
结论:
指针类型决定了指针在解引用时,可以访问多少个字节(即指针的权限)
2.指针类型决定指针在加减1时的步长(即跨过多少字节)
讲解代码:
分析:
整型指针加1,跳过4个字节;
字符指针加1,跳过1个字节;
结论:
指针类型决定指针在加减1时的步长(即跨过多少字节)
如果是 type * p 加减n ,就会跳过sizeof(type)*n 个字节
例如 int * p 4 * n 个字节
四.野指针
1.野指针的概念:
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
2.野指针的成因
1.指针未初始化
2.指针越界访问
3. 指针指向的空间释放
3. 如何规避野指针
1.指针初始化
当明确知道指针放谁的地址就直接初始化谁的地址;
不知道初始化为谁的地址就暂时初始化为NULL(空指针);
2.防止指针越界;
3.当指针指向的空间释放,即使置为NULL
4.避免返回局部变量的地址
5.指针使用之前检查有效性
五.指针运算
1.指针加减整数
前面在指针类型的意义那有讲解到加减n;
示例:用指针打印数组
2.指针-指针
指针-指针其实就是地址-地址;
指针-指针的前提是:都指向的是同一块空间
指针-指针得到的数值的绝对值(因为可能为负数)是:两指针之间的元素个数
示例:
3.指针的关系运算
标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。(如下面第二个图中代码)
图一:
图二:
六.指针和数组
1.指针和数组的关系
指针变量就是指针变量,不是数组,指针变量的大小是4 / 8个字节,是专门用来存放地址的;
数组就是数组,不是指针,是一块连续的空间,可以存放一个或多个相同类型的数据;
联系:
数组名是首元素地址(两种特殊情况除外,sizeof(数组名)和函数(数组名)),数组名是地址,数组名==地址==指针;
2.通过指针遍历数组
当我们知首元素地址时,又因为数组时连续存放的,所以通过指针就可以遍历访问数组;
证明:
#include <stdio.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,0 }; int* p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; for (i = 0; i < sz; i++) { printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i); } return 0; }
所以 p+i 其实计算的是数组 arr 下标为i的地址;
那我们就可以直接通过指针来访问数组;
七.二级指针
是变量就会有地址,一级指针变量也是有地址的,存放一级指针地址的变量就是二级指针;