指针初级详解
1. 指针是什么 指针是个变量,存放内存单元的地址
2. 指针和指针类型
3. 野指针
4.指针运算
5. 指针和数组
6. 二级指针
7. 指针数组
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;
//在这里我们对变量a,取出它的地址,可以使用&操作符。
//将a的地址存放在p变量中,p就是一个指针变量
//总结:指针就是变量,用来存放地址的变量,(存放在指针中的值,都被当成地址处理)
总结:指针是用来存放地址的,地址是唯一标示一块地址空间的
总结:指针的大小在32位平台是4个字节,在64位平台上是8个字节
return 0;
}
- 总结:指针是用来存放地址的,地址是唯一标示一块地址空间的
- 总结:指针的大小在32位平台是4个字节,在64位平台上是8个字节
指针类型的第一个意义 解引用操作
int main()
{
/*printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(double*));*/
int a = 0x11223344;//16进制两个数是1个字节
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);//006FF920
printf("%p\n", pc);//006FF920
/*
&a无论放到 int* 还是char*中都可以存放a的地址
但是它们有什么区别呢
*/
*pa = 0;//是int
*pc = 0;//是char
/*
但是对它们进行解引用操作的时候 *pa可以改4个字节内容
就可以改成00 00 00 00
但是*pc是只可以改一个字节内容,因为是char型的
就会改成 00 33 22 11 只会改一个字节的内容
所以指针类型决定了指针进行解引用操作的时候能够访问空间的大小
int* p ; *p能够访问4个字节
char* p ; *p能够访问1个字节
double* p ; *p能够访问8个字节
*/
return 0;
}
总结: 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节),比如
char* 的指针解引用就只能访问一个字节,而int*的指针的解引用就能访问4个字节
指针类型的第二个意义 指针+整数
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pb = &a;
printf("%p\n", pa); //00EFFA28
printf("%p\n", pa + 1);//00EFFA2C 这里加了1因为pa是整型所以加了4个字节8+4=c c是12
printf("%p\n", pb); //00EFFA28
printf("%p\n", pb + 1);//00EFFA29这里加1是因为pb是char型所以加了1个字节8+1=9
指针类型决定了:指针走一步走多远(指针的步长)
int* p; p+1 -->4个字节 +1意思是加了一个整型
char* p p+1 -->1个字节 +1意思是加了一个字符型
double* p p+1 -->8个字节 +1意思是加了一个双精度型
return 0;
总结;指针的类型决定了指针向前或者向后走一步有多大(距离)
}
总结;指针的类型决定了指针向前或者向后走一步有多大(距离)
野指针
野指针:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
野指针成因 :
1. .指针未初始化
2. 指针越界访问
3. 指针指向的空间释放
1.指针未初始化
int main()
{
// int a;//局部变量不初始化,默认是随机值
int *p;//局部的指针变量,不初始化,随机值
return 0;
}
2.指针越界访问
int main()
{
int arr[10] = {
0 };
int* p = arr;//数组名首元素的地址
int i = 0;
for (i = 0; i < 12; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3.指针指向的空间释放
int* test()
{
int a = 10;
return &a;
}
//这里的a是局部变量 调用test()函数的时候 进入变量创建,出了变量销毁
//销毁之后,内存空间就还给了操作系统a的空间就不在了
int main()
{
int *p = test();
//这里确实接受了a的地址,但是返回来之后,函数已结束空间就还给系统了
//p所指向的空间已经还给操作系统了,
*p = 20;//*p进行解引用的时候访问的空间已经不是p的空间了,变成了其它的空间了
return 0;
}
这是一个指针越界访问的问题
int* test()
{
int a = 10;
return &a;
}//这里的a是一个局部变量,访问一次之后它就会自动销毁。这里返回的是a的地址
int main()
{
int* p = test();//这里的p是指针变量,用来接收a的地址
printf("%d\n", *p);//10
return 0;
}
如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置为null
4. 指针使用之前检查有效性
int main()
{
//int a = 10;
//int* pa = &a;//1.指针初始化
//int* p = NULL;//null
int a = 10;
int *pa = &a;
*pa = 20;
pa = NULL;//这是pa不指向a避免称为野指针..
//3.指针指向空间释放即使置为null
if (pa != NULL)
{
//4.指针使用之前检查有效性
}
return 0;
}
指针运算
1. 指针+整数
2. 指针-整数
3. 指针的关系运算
指针加整数
int main()
{
int arr[10] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//for (i = 0; i < sz; i++)
//{
// printf("%d\n", *p);//1,2,3,4,5,6,7,8,9,10
// p = p + 1;
//}
for (i = 0; i < 5; i++)
{
printf("%d\n", *p);//1,3,5,7,9
p+=2;
}
return 0;
}
指针减整数
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10};
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[9];//这里的&arr[9]拿的就是第10个元素的地址
for (i = 0; i < 5; i++)
{
printf("%d\n", *p);
p -= 2;
}
return 0;
}
指针是可以进行关系运算的
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float *vp;
//指针+-整数:指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
return 0;
}
指针减去指针 指针-指针
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10};
printf("%d\n", &arr[9] - &arr[0]);//9个
//指针减去指针,得到的是中间元素的个数
return 0;
}
strlen - 求字符串长度的
递归 - 模拟实现了strlen - 计数器的方式,递归的方式
int my_strlen(char* str)
{
char* start = str;
char* end = str;
while (*end != '\0')
{
end++;
}
return end - start;
}
int main()
{
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
指针的关系运算 就是比大小
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float *vp;
//尽量使用这种写法,
for (vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;//先进行--vp操作然后在解引用每次向左移动一个
}
//尽量不要用这种写法
for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--)
{
*vp = 0;
}
/*c语言标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许
与指向第一个元素之前的那个内存位置的指针进行比较
*/
return 0;
}
c语言标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
下图就说明了
指针和数组
int main()
{
int arr[10] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("%p\n", arr);//数组名 - 首元素的地址 00EFF8E0
printf("%p\n", arr+1); //00EFF8E4
printf("%p\n", &arr[0]); //00EFF8E0
printf("%p\n", &arr[0] + 1); //00EFF8E4
printf("%p\n", &arr); //00EFF8E0 取出的是整个数组的地址
printf("%p\n", &arr+1); //00EFF908 这里因为是加1移动的是整个数组所以移动大
/*
1. &arr - &数组名- 这里的数组名不是首元素的地址了- 数组名表示整个数组
- &数组名 取出的是整个数组的地址
2. sizeof(arr)-sizeof(数组名)- 数组名表示的整个数组-
sizeof(数组名)计算的是整个数组大小
*/
return 0;
}
通过数组打印
通过指针打印
int main()
{
int arr[10] = {
0 };
int* p = arr;
int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%p ===== %p\n",p+i, &arr[i]);
//}
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);//通过数组来打印
printf("%d ", *(p + i));//通过指针来打印
}
return 0;
}
通过指针来访问数组
int main()
{
int arr[10] = {
0 };
int i = 0;
int* p = arr;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
printf("%d ", *(p + i));
}
总结:p+i其实计算的是数组arr下标为i的地址
: 那我们就可以直接通过指针来访问数组
return 0;
}
实例
实例
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("%d ", *(p+i));
}
return 0;
}
二级指针
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针
**ppa = 20;
printf("%d\n", **ppa);
printf("%d\n", a);
return 0;
}
指针数组 – 数组
数组指针 – 指针
整型指针数组
int main()
{
int a = 10;
int b = 20;
int c = 30;
//int* pa = &a;
//int* pb = &b;
//int* pc = &c;
//整型数组 - 存放整型
//字符数组 - 存放字符
//指针数组 - 存放指针
//int arr[10];//数组
int* arr2[3] = {
&a,&b,&c};// 这就是整型指针数组
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", *(arr2[i]));
}
return 0;
}