1. 指针是什么
指针理解的2个要点:
-
指针是内存中一个最小单元的编号,也就是地址
-
平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量。
指针变量
我们可以通过&(取地址操作符)取出变量的内存其实是地址,把地址可以存放到一个变量中,这个变量就是指针变量
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
printf("%p\n", &a);
printf("%p\n", pa);
*pa = 20;//* - 解引用操作
printf("%d\n", a);
return 0;
}
那这里的问题是:
-
一个小的单元到底是多大?(1个字节)
-
如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:
0000000000000000000000000000000000000000000000000000000000000001...011111111111111111111111111111111000000000000000000000000000000010000000000000000000000000000001...11111111111111111111111111111111
这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。
同样的方法,那64位机器,如果给64根地址线,那能编址多大空间?
这里我们就明白:
-
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。
-
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结: 指针是用来存放地址的,地址是唯一标示一块地址空间的。 指针的大小在32位平台是4个字节,在64位平台是8个字节。
2. 指针和指针类型
我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:有的。
指针类型其实是有意义的
-
指针类型决定了,指针进行解引用操作的时候,一次性访问几个字节,访问权限的大小
如果是char*的指针,解引用访问1个字节
如果是int*的指针,解引用访问4个字节
思考下列代码会输出什么结果?
int main()
{
int a = 0x11223344;
char* pc = (char*)&a;
int i = 0;
for (i = 0; i < 4; i++)
{
*pc = 1;
pc++;
}
return 0;
}
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
1. 指针未初始化
2. 指针越界访问
3. 指针指向的空间释放
3.2 如何规避野指针
-
指针初始化
-
小心指针越界
-
指针指向空间释放即使置NULL
-
避免返回局部变量的地址
-
指针使用之前检查有效性
4. 二级指针
int main()
{
int a = 10;
int * pa = &a;//pa是一级指针变量
int* * ppa = &pa;//ppa就是一个二级指针变量
**ppa = 50;
printf("%d\n", **ppa);
printf("%d\n", a);
//int** * pppa = &ppa;//pppa是一个三级指针变量
return 0;
}
5. 指针数组
字符数组就是放数字的数组,整型数组就是放整形的数组,那么,指针数组就是放指针的数组。
思考:如何用一维数组,模拟一个二维数组?
int main()
{
//假设我想模拟出一个3行4列的数组
int a[] = { 1,2,3,4 };
int b[] = { 2,3,4,5 };
int c[] = { 3,4,5,6 };
int* arr[3] = { a, b, c };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}