1. 指针是什么
指针是什么? 指针理解的2个要点:
- 指针是内存中一个最小单元的编号,也就是地址
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
int main()
{
int a = 10;//a是整型变量,占用4个字节的内存空间
return 0;
}
指针变量,用来存放地址的变量。
(存放在指针中的值都被当成地址处理)。
那这里的问题是:
1、一个小的单元到底是多大?(1个字节)
2、如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
…
11111111 11111111 11111111 11111111
这里就有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. 指针和指针类型
前提:
x86 - 32位环境
x64 - 64位环境
int main()
{
char* pc = NULL;
short* ps = NULL;
int* pi = NULL;
double* pd = NULL;
sizeof 返回的值的类型是无符号整型 unsigned int 下面应该是%zu 无伤大雅
printf("%d\n", sizeof(pc));
printf("%d\n", sizeof(ps));
printf("%d\n", sizeof(pi));
printf("%d\n", sizeof(pd));
return 0;
}
在x86里面结果都是4 故:不论类型是什么 指针的大小都一样
指针类型的意义
int main()
{
int a = 0x11223344;
//两个十六进制位对应一个字节,正好一个字节放得下
//int* pa = &a;
//*pa = 0;
//通过地址找到变量a 然后进行变量的修改
char* pc = (char*)&a;
指针类型有意义的 传入了a的首元素地址 后面修改的只有第一个字节
前面第76和77行的代码进行修改的时候是4个字节一起修改
指针类型的差异决定了被解引用操作的时候访问的字节数
return 0;
}
结论:
指针类型决定了指针在被解引用的时候访问几个字节
如果是int类型的指针 解引用访问4个字节
如果是char类型的指针 解引用访问1个字节
100
0x64 - 16进制
110 0100 - 2进制
144 - 8进制
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = (char*)&a;
printf("pa = %p\n", pa);
printf("pa+1 = %p\n", pa+1);
printf("pc = %p\n", pc);
printf("pc+1 = %p\n", pc+1);
return 0;
}
结论:
指针的类型决定了指针+1或-1操作跳过几个字节
决定了指针的步长
int main()
{
int a = 0;
int* pi = &a;//pi解引用访问4个字节 pi+1也是跳过4个字节
float* pf = &a;//pf解引用访问4个字节 pi+1也是跳过4个字节
int*和float*是否可以通用?
不可以 ,看似两者权限一样
*pi = 100;
*pf = 100.0;
这个时候监视a的值就不一样了
return 0;
}
3. 野指针
成因
int main()
{
int* p;
//p没有初始化,就意味着没有明确的指向
//一个局部变量没有初始化的话,放的是随机值:0xcccccccc
//所以这是一个非法的地址
*p = 10;//非法访问内存
return 0;
}
指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;//&arr[0]
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
指针指向的空间释放
void* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
return 0;
}
int main()
{
int a = 10;
int* p1 = &a;
//null--->0
int* p2 = null;
*p2 = 100;//err
int* p3 = null;
if (p3 != null)
{
*p3 = 100;//ok
}
return 0;
}
如何规避野指针?
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
4. 指针运算
指针±整数;指针的关系运算
int main()
{
#define N_VALUES 5
float values[N_VALUES];
float* vp;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
}
int main()
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//for (i = 0; i < sz; i++)
//{
// *p = 1;
// p++;
//}
for (i = 0; i < sz; i++)
{
*(p + i) = 1;
}
return 0;
}
另一种将数组全部赋值为1的方式
指针减指针的绝对值----->得到的是元素个数
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
不是所有的指针都能相减
指向同一块空间的两个指针才能相减
用法:
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
递归版本:
int my_strlen(char* str)
{
int count = 0;
char* p = str;
while (*str != '\0')
{
count++;
}
return (count-p);
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
指针相加--->没有意义
5. 指针和数组
int main()
{
int a = 10;
int* pa = &a;//pa是一个指针变量,是一个一级指针变量
int** ppa = &pa;//ppa是一个二级指针变量
**ppa = 20;//通过两次解引用找到a
*pa = 20;
printf("%d\n", a);
return 0;
}
int * pa --->*代表:pa是一个指针变量 int代表 :pa指向的类型是int类型
int** pa --->第二个*代表ppa是一个指针变量 前面int*表示ppa指向的对象pa是一个int*类型
二级指针是用来存放一级指针变量的地址的
//指针数组
//存放指针的数组就是指针数组
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* pa = &a;
int* pb = &b;
int* pc = &c;
如果整型变量太多 一个个去创建的话比较麻烦
int* parr[3] = {&a,&b,&c};
数组里面放的是整型指针类型
int i = 0;
for (i = 0; i < 3; i++)// 0 1 2
{
printf("%d ", *parr[i]);
}
//用指针数组模拟二维数组并打印
int arr1[4] = { 1,2,3,4 };
int arr2[4] = { 2,3,4,5 };
int arr3[4] = { 3,4,5,6 };
int* parr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
//arr
return 0;
}
//模拟一个二维数组并且打印
int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
1 2 3 4
2 3 4 5
3 4 5 6
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
6. 二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
这就是 二级指针 。
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa
*ppa 其实访问的就是 pa .
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppa 先通过 *ppa 找到 pa
然后对 pa 进行解引用操作: *pa ,那找到的是 a
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
7. 指针数组
指针数组是指针还是数组?
答案:是数组。是存放指针的数组。
那指针数组是怎样的?
int* arr3[5];//是什么?
arr3是一个数组,有五个元素,每个元素是一个整形指针。