目录
一. 指针是什么
1.指针变量
2.如何编址?
二. 指针和指针类型
三. 野指针
3.1 野指针成因
3.2 如何规避野指针
四. 指针运算
4.1指针加减整数
4.2指针减指针
4.3指针的关系运算
五. 指针和数组
六. 二级指针
七. 指针数组
1)指针是内存中一个最小单元的编号,也就是地址
2)平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个
变量就是指针变量(专门用来储存地址的)总结:
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
a.一个小的单元到底是多大? Ans:1个字节
b.经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于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个字节
整型指针操作4个字节
字符指针操作1个字节
总结:
指针类型决定了,指针在被解引用时的访问权限
#include
//演示实例
int main()
{
int n = 10;
char *pc = (char*)&n;
int *pi = &n;
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
指针类型决定了,指针向前或这向后走一步,走多大距离
int *+1 ----> +1*sizeof(int) == +4
char *+1 ----> +1*sizeof(char) == +1
int main()
{
int arr[10] = { 0 };
//如果希望按照一个整型的形式访问
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*p = 0x11223344;
p++;
}
//假设你希望,你访问这个40个字节的时候,是以字节为单位访问
char* p = (char*)arr;//int*
int i = 0;
for (i = 0; i < 40; i++)
{
*p = 'x';
p++;
}
return 0;
}
补充:
1.不管是二维还是一维,都不会去创建数组,所以数组的大小不需要明确指定
一维数组传参时,形参的数组大小可以忽略。
二维数组传参的时候,行可以省略,列不可以
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
1. 指针未初始化
2.指针越界使用
3.指针指向空间释放
1. 指针初始化
当指针初始化时,注意‘ a ’所指的为一个整数ASCII值,“ a ”才是一个指针
所以char* a = 'arr';不正确;应改为char* a = "arr";
2. 小心指针越界(不操作,仅仅指向外部某条件)
3. 指针指向空间释放即使置NULL(零地址数向下一段空间用户不能访问)
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
#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 };
printf("%d\n", &arr[0] - &arr[9]);
return 0;
}
指针—指针得到的是指针和指针之间元素的个数,但两个指针指向的是同一个空间
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
ps.可以向前越界,但不能向后越界
#include
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
数组名的两个例外
1.sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
2.&数组名,数组名表示整个数组,取出的是整个数组的地址
原因:
1.&数组和数组名为连续的空间
2.&数组名为首元素地址
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", arr+1);//int*
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);//int*
printf("%p\n", &arr);
printf("%p\n", &arr+1);//arr指向一个数组共10*4个字节=40
return 0;
}
输出:
0133F840 +4
0133F844
0133F840 +4
0133F844
0133F840 +40
0133F868
int main()
{
int a = 10;
int* pa = &a;//pa是指针变量(一级指针)
int** ppa = &pa;//ppa是一个二级指针
int*** pppa = &ppa;//pppa就是三级指针
return 0;
}
int *pa : 1.*告诉我们pa是指针 2.int说明pa指向int类的数据
int **pa :1.*告诉我们ppa为指针 2.int* 告诉我们ppa指向对象为int *类型
定义:指针数组 - 存放指针的数组
int main()
{
int data1[] = { 1,2,3,4,5 };
int data2[] = { 2,3,4,5,6 };
int data3[] = { 3,4,5,6,7 };
//arr就是一个指针数组
int* arr[3] = { data1 ,data2, data3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}