本节知识点:
1.指针是什么
2.指针和指针类型
3.野指针
4.指针运算
5.指针和数组
6.二级指针
7.指针数组
一、指针是什么
指针理解的两个要点:
1.指针是内存中一个最小单元单元的编号,也就是地址
2.平时口语中说的指针,通常指的是指针变量,用来存放地址的变量
下面我们来详细解释一下指针:
内存是一块很大的空间,为了更好的管理,我们把它划分成一个个小的内存单元,一个基本的内存单元的大小是一个字节。我们把每一个内存单元都进行编号,就像对房间进行编号,这个编号就称为地址,这样我们就能快速的找到我们所要寻找的内存单元。
而内存单元的编号/地址/指针表达的都是一个意思
拓展补充地址编号怎么产生:
32位机器----有32个地址线,就是物理的电线--对电线通电与否,就会产生0/1两种结果,即电信号转化为数字信号,就会产生如下的结果:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
......
01111111 11111111 11111111 11111111
10000000 00000000 00000000 00000000
......
11111111 11111111 11111111 11111111
这些二进制序列就作为编号
二、指针和指针类型
1、指针变量
我们通过&(取地址操作符)取出变量的内存起始地址,把地址存放在一个变量中,这个变量就是指针变量。即指针变量,用来存放地址的变量。
指针变量的大小:
在32位的机器上,地址是32个0/1组成的二进制序列,那么地址就需要4个字节的空间来存储,所以一个指针变量的大小就是4个字节。(1字节=8 bit,所以32 bit=4字节)
同理,在64位机器上,一个指针变量的大小是8字节。
2、指针类型
既然指针大小都是固定的,那定义指针类型的意义在哪呢?
第一个意义:指针类型决定了在解引用时一次能访问几个字节(指针的权限)
下面我们看这两个代码
#include <stdio.h>
int main()
{
int a =0x11223344;
int* pa = &a;
*pa = 0;
char* pc = &a;
*pc = 0;
//指针类型决定了在解引用时一次能访问几个字节(指针的权限)
//int* 4个字节
//char* 1个字节
//double* 8个字节
}
那么这个有什么作用呢?
如在一块内存中,如果我们想要访问四个字节,我们可以使用 int* / float*型指针;如果我们想要访问一个字节,我们可以使用 char*型指针。
第二个意义:决定了指针向前或向后走一步,走多大距离(单位是字节)
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pa+1);
printf("%p\n", pc);
printf("%p\n", pc+1);
return 0;
}
这个是上述代码的运行结果:
三、野指针
3.1 野指针的成因
1.指针未初始化
int main()
{
int* p; //局部变量未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++) //当前指针指向的范围超出数组arr的范围时,p就是野指针
{
*p = i;
p++;
}
return 0;
}
3.指针指向的空间释放了
int* test()
{
int a = 100;
return &a;
}
int main()
{
int* p=test();
printf("%d", *p);//出了test函数,a就被销毁了
return 0;
}
这里的销毁不是真正意义上的销毁,是使用权还给系统了,比如这里p依然指向a的地址,但a的值可能变了,不一定是100了,是系统随机分配的值
3.2 如何规避野指针
1.指针初始化:两种初始化形式
int main()
{
int a = 0;
int* pa = &a;//明确知道指向谁
int* p = NULL;//不知道指向谁是,就指向NULL
return 0;
}
2.小心指针越界
3.指针指向空间释放及时置NULL
4.避免返回局部变量的地址
5.指针使用之前检查有效性
判断有效性的语句:if(p != NULL)
四、指针运算
1.指针+-整数
例题:利用指针来打印10-1
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
int* q = &arr[9];
for (i = 0; i < 10; i++)
{
printf("%d ", *q);
q--;
}
return 0;
}
输出结果如下:
2.指针-指针
指针-指针的前提是:两个指针指向同一块空间
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]); //两个地址相减,减出来是两个地址间元素的个数
printf("%d\n", &arr[0] - &arr[9]);
return 0;
}
输出结果如下:
3.指针的关系运算(指针比较大小)
for (vp = &valuse[N_VALUSE]; vp > &valuse[0];)
{
*--vp = 0;
}
五、指针和数组
数组和指针本身就是两种事物,联系就是可以通过指针访问数组
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%p == %p\n", p + i,&arr[i]);
}
}
输出结果如下:
六、二阶指针
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是一个二级指针
int*** pppa = &ppa;//pppa就是一个三级指针
**ppa = 20;
printf("%d\n", a);//通过二级指针访问a
return 0;
}
pa存放的是a的地址
ppa存放的是pa的地址
pppa存放的是ppa的地址
七、指针数组
int main()
{
int arr[10];//整型数组--存放整型的数组
char ch[5];//字符数组--存放字符的数组
int a = 10;
int b = 20;
int c = 30;
int* arr2[5] = {&a, &b, &c};//指针数组--存放指针的数组
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", *(arr2[i]));
}
return 0;
}
输出结果如下: