指针是什么?
理解要点:
- 指针是内存中一个最小单元的编号(地址)
- 口头语中说的指针,通常指的是指针变量
32位虚拟地址空间
CPU——32位地址——地址线传输—>内存 4GB
4byte
64位虚拟地址空间
CPU——64位地址——地址线传输—>内存
8byte
指针变量
通过&(取地址操作符)取出变量的真实地址,把地址存放到一个变量中,这个变量就是指针变量。
int main()
{
int a = 10;//在内存中开辟一个空间
int* p = &a;//a变量占4个字节的空间(整型),这里将a的4个字节中的第一个字节地址存放在p变量中,p就是一个指针变量
return 0;
}
p是一个变量,专门用来存放地址的,这个变量就被称为指针变量
总结:
指针变量,就是用来存放地址的变量。(存放在指针中的值都被当成地址处理)
一个小的单元是一个字节
指针和指针类型
指针大小 4字节 8字节
指针类型决定指针在被解引用的时候访问的权限
整型指针解引用访问4个字节
字符指针接引用访问1个字节
……
指针类型决定指针向前或向后走一步走多大距离
int * +1 --> +1*sizeof(int) == +4
char * +1 --> +1*sizeof(char) == +1
int * +n --> +n*sizeof(int)
char * +n --> +n*sizeof(char)
sizeof(long)==>sizeof(int)
32位:sizeof(long) 4字节
64位:sizeof(long) 8字节
野指针
概念:指针指向的位置是不可知的(随机的,不正确的,没有权限的)
成因:
- 指针未初始化(默认为随机值)
- 指针越界访问
- 指针指向的空间释放
//1
int main()
{
int* p;
//可以给一个空指针int* p=NULL;
*p = 20;
return 0;
}
//2
int main()
{
int arr[5] = { 1,2,3,4,5 };
int i = 0;
int* p = arr;
for (i = 0; i < 10; i++)
{
printf("%d ", *p);
p++;
}
return 0;
}
//3
int* test()
{
int a = 10;
printf("%d\n", a);
return &a;//a已经销毁
}
int main()
{
int*p = test();
*p = 100;
return 0;
}
如何规避野指针:
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
int main()
{
int a = 10;
int* p = &a;
int* q = NULL;
if(q !=NULL)
{
//...
}
p =NULL;
return 0;
}
指针运算
指针加减整数
#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[9] - &arr[0]);//9
return 0;
}
//my_strlen
//1. 计数器的方法
//2. 递归
//3. 指针-指针
int my_strlen(char* str)
{
char* start = str;
while (*str)
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);//6
return 0;
}
指针关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
//代码简化:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较
指针和数组
数组名是数组首元素的地址
但是有2个例外:
sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
&数组名,数组名表示整个数组,取出的是整个数组的地址
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 0 1 2 3 4 5 6 7 8 9
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//1 2 3 4 5 6 7 8 9 10
}
//printf("%p\n", arr);//
//printf("%p\n", &arr[0]);
//printf("%d\n", sizeof(arr));
return 0;
}
数组是一块连续的空间
指针是存放地址的变量
可以通过指针访问数组
二级指针
int main()
{
int a = 10;
int* pa = &a;//pa是指针变量(一级指针)
//*告诉我们pa是指针 int说明pa指向的是int类型的数据
int** ppa = &pa;//ppa是一个二级指针
//第二颗*告诉我们ppa是指针 int*说明ppa指向的是int*类型的数据
int*** pppa = &ppa;//pppa就是三级指针
return 0;
}
*ppa-->pa
*pa-->a
* *ppa-->a
指针数组
//指针数组 - 存放指针的数组
//整型数组
//一维数组
int main()
{
//int arr[5];//指针数组 - 存放整型的数组
//char ch[6];//字符数组 -存放字符的数组
int a = 10;
int b = 11;
int c = 12;
int d = 13;
int e = 14;
int* arr2[5] = {&a, &b, &c, &d, &e};//指针数组
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(arr2[i]));//10 11 12 13 14
}
return 0;
}
//二维数组
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;
}
打印结果:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
指针数组
int* arr3[5];
arr3是一个数组,有五个元素,每一个元素是一个整形指针