指针
指针是个变量,存放内存单元的地址
指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//将a的地址存放在p变量中,p就是一个之指针变量。
return 0;
}
- 指针的编址空间
32位平台下- - -4个字节- - -2^32空间
32位平台下- - -8个字节- - -2^64空间
注意:一个字节有8个比特位
指针类型的影响
1、指针类型决定了,指针走一步,能走多远
int main()
{
int arr[10] = { 0 };
int *p = arr;
char *pc = arr;
printf("%p\n", p);
printf("%p\n", p + 1);
printf("%p\n", pc);
printf("%p\n", pc + 1);
}
2. 指针类型决定了:指针解引用的权限有多大
int a = 0x11223344;
地址本来是如图这样的
char类型的解引用
int a = 0x11223344;
char* pc = &a;
*pc = 0;
解引用后的图片:char类型解引用后只能改变一个比特位
int类型的解引用
int a = 0x11223344;
int* pa = &a;
*pa = 0;
解引用后的图片:int类型解引用后能改变4个比特为
3、指针解引用+访问类型不同时
int main()
{
int arr[10] = { 0 };
int *p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
return 0;
}
arr-- – 初始化的地址
arr-- – 运行后的地址
int四个字节(四个字节为1个数)- -跳过四个字节赋值为1
当你需要一个个字节的访问的时候,用char
int main()
{
int arr[10] = { 0 };
char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
return 0;
}
arr-- – 初始化后的地址
arr-- – 代码运行后的地址
野指针
- 指针未初始化
int main()
{
int* p; //p是一个局部的指针变量,局部变量不初始化的话,默认是随机值
*p = 20; //非法访问内存了
}
- 指针越界访问— --输出会出现错误
第一种情况
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*p = i;
p++;
}
return 0;
}
越界图:
地址是由低----------------------------------------------高变换的
第二种情况
int* test()
{
int a = 10;
return &a;
}
int main()
{
int*p = test();
*p = 20;
return 0;
}
越界图:
如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL
- 指针使用之前检查有效性
int main()
{
//当前不知道p应该初始化为什么地址的时候,直接初始化为NULL
//int* p = NULL;
//明确知道初始化的值
//int a = 10;
//int* ptr = &a;
//C语言本身是不会检查数据的越界行为的
//空指针出现问题
int* p = NULL;
*p=10
//空指针出现问题,所以检查指针有效性
if(p != NULL)
*p = 10;
return 0;
}
指针运算
指针 + 整数
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int* pend = arr + 9;
while (p<=pend)
{
printf("%d\n", *p);
p++;
}
return 0;
}
指针 - 指针
**指针减去指针得到两个指针间的元素个数
情况1
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
char c[5];
//指针和指针相减的前提:
//两个指针指向同一块空间
//printf("%d\n", &arr[9] - &c[0]);//这里一个是char类型一个是int所以会出错--err
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
情况2
#include <string.h>
//计数器方法
//int my_strlen(char* str)
//{
// int count = 0;
// while (*str != '\0')\\指向\0的时候结束
// {
// count++;
// str++;//边++边指针下一个字符
// }
// return count;
//}
//指针-指针的方法
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;//-- --- --- --- -- ---指针减去指针
}
//还有递归的方法
int main()
{
int len = my_strlen("abc");//传过去的是首元素
printf("%d\n", len);
return 0;
}
指针关系运算
指针、数组
数组名是数组首元素的地址 arr,&arr[0]
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
//printf("%p <==> %p\n", &arr[i], p + i);
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
一些简单的变换
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int* p = arr;//数组名
printf("%d\n", arr[2]);
printf("%d\n", p[2]);//p[2] --> *(p+2)
//[] 是一个操作符 2和arr是两个操作数
//a+b
//b+a
printf("%d\n", 2[arr]);
printf("%d\n", arr[2]);
//arr[2] --> *(arr+2)-->*(2+arr)-->2[arr]
//arr[2] <==> *(arr+2) <==> *(p+2) <==> *(2+p) <==> *(2+arr) == 2[arr]
//2[arr] <==> *(2+arr)
return 0;
}
二级指针
二级指针指向一级指针的地址
int main()
{
int a = 10;
int* pa = &a;//pa是指针变量,一级指针
//ppa就是一个二级指针变量
int * * ppa = &pa;//pa也是个变量,&pa取出pa在内存中起始地址
int** * pppa = &ppa;
return 0;
}