目录
前言:这里简单回顾一下之前浅聊指针的主要内容
- 内存被划分成一个个的内存单元,每个内存单元的大小是1个字节
- 每个字节的内存单元都有一个编号, 这个编号就是地址,地址在C语言中称为指针
- 地址要存储的话,存放在指针变量中
- 每个内存单元都有一个唯的地址来标识
- 32位机器上地址的大小是4个字节,所以指针变量的大小也是4个字节;同理,在64位机器上地址的大小是8个字节,所以指针变量的大小也是8个字节
1.指针变量
概念:我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个 变量就是指针变量
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟4个字节空间,用来存放10
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
//中,p就是一个之指针变量。
return 0;
}
总结:指针变量,用来存放地址的变量。(存放在指针变量中的值都会被当作地址处理)
2 .指针的类型
-
解引用操作
下面对比两组代码:
int main( )
{
int a = 0x11223344;
int* p=&a;
*p = 0;
return 0;
}
int main( )
{
int a = 0x11223344;
char* p = &a;
*p = 0;
return 0;
}
第一组代码调试结果:
第二组代码调试结果:
总结:指针类型是有意义的,指针类型决定了指针进行解引用操作的时候,会访问几个字节
-
指针加减整数
int main()
{
int a = 0;
int* pa = &a;
char* pc = &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);
}
观察结果:
总结:又表明,指针类型是有意义的,指针类型决定了指针进行+1或-1,跳过几个字节
int* 跳过4个字节
char* 跳过1个字节
short* 跳过2个字节
double* 跳过8个字节
-
指针类型在数组中的应用
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//数组中每个元素的类型是int
return 0;
}
来看不同类型的指针访问过程:
总结:在访问数组时,指针也会因类型的不同导致访问内存大小的不同
3.野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
-
指针未初始化
示例:
int main()
{
int* p;
*p=10;
return 0;
}
-
指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*p = -1;
p++;
}
return 0;
}
当指针指向的范围超出数组arr的范围时,p就是野指针
-
指针指向的空间释放
int* test()
{
int a = 10;//创建局部变量10
return &a;
}
int main()
{
int* p = test();//这里p为野指针
//出函数返回a的地址,但是a所申请的内存空间出函数后被回收
//即使可以找到那块空间的地址,但是那块空间也不属于你
printf("%d\n", &p);
return 0;
}
-
如何规避野指针
1.指针初始化
如果明确指针应该指向哪里,就初始化正确的地址。
int a=10;
int* p=&a;
如果不知道初始化为什么值,为了安全,初始化为NULL
int* p=NULL;
2.小心指针越界
3.指针指向空间释放,及时将其置成NULL
注:NULL--0,而0作为地址时,用户程序是不能访问的
4.避免返回局部变量的地址
5.指针使用之前检查有效性
4.指针的运算
-
指针 + - 整数
//使用指针打印数组内容
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//数组名为数组首元素地址
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));//数组中下标为i元素的地址
// 从内存角度分析,就是跳过了i*sizeof(int)个字节
}
return 0;
}
结果为:1 2 3 4 5 6 7 8 9 10
扩展
结合上方代码,可以得出:
arr ==p
arr+i == p+i
*(arr+i) ==*(p+i) == arr[i]
*(i+arr) ==i[arr]<--加法交换律
-
指针-指针
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
}
观察结果:
总结:指针-指针前提:两个指针指向同一块区域,指针类型相同
指针-指针差值的绝对值是指针和指针之间的元素个数
-
应用
例:写一个函数用来实现strlen的功能
size_t my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
5.指针的关系运算
标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
下面来看两种写法:
第一种:
#define N_VALUES 5
float values[N_VALUES];
float *vp;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
过程:
第二种:
#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
过程:
注:第二种写法不符合标准规定 ,在不同的编译器上可能会出现报错的风险。
6.二级指针
int main()
{
int a = 10;
int* p = &a;//p是一级指针变量
int* *pa = &p;//pa是二级指针变量
return 0;
}
理解1:int**pa
pa是个指针变量,*pa(解引用操作)拿到的是指针变量p(里面是a的地址),而p的类型是int*
理解2:int* *pa
*pa说明pa是个指针变量,而它所指向的元素类型(也就是p的类型)是int*
这样如果我们想更改变量a的值的话:
理解: **pa=20
pa是个指针变量,*pa(解引用操作)拿到的是指针变量p(里面是a的地址),总之还是地址;**pa相当于再对p进行(解引用操作),拿到的也就是变量a了,再进行赋值操作就更改了a的值
7.指针数组
概念:指针数组指的是存放指针的数组,是数组。
举例:
char* arr[5];//存放5个字符指针的数组
int* arr[5];//存放5个整形指针的数组