一:指针基本概念:
(一)基础概念:地址和内存:
地址和内存:
地址类似于一个编号,一个标识,它相对来说具有唯一性。
内存是计算机内部的一块儿空间。
例如:学生宿舍的编号就是地址,宿舍本身就是一个内存。通过宿舍编号能够找到学生宿舍,如图,里面的编号就是地址,通过每个地址就能找到一个对应的小格子(内存)。
地址就是内存的编号,通过地址能够查找到相应空间的内存。
(二)指针相关基本概念理解:
指针:可以说指针就是地址,地址就是指针,把他们当作一个相同的概念来理解。
指针变量:用来存储指针(地址)的变量。它和其他变量(int ,char,short,float,double,long,long...)是一回事儿,表示形式:(其他变量类型) 加 * 变量名字。举例:int * ,char * ,float*等等。
&操作符:&用来获取空间的地址,也就是上面说的编号。
*(解引用操作符):用来找到指针变量指向的内容。
指针类型:去掉变量名剩下的部分。
有了上面的简单解释之后看代码和注释:
//vs环境下
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int num = 12;
char* p = #//去掉p得到char*,char*就是指针的类型,p是指针的名字。//这里&得到了num这个内存空间的地址,将它存在p里面
printf("%d\n",*p);//*找到p指向的地址里面的内容
printf("%d\n",num);
int size = sizeof(p);
printf("指针变量的大小是:%d\n", size);
//sizeof(int),sizeof(char),我们知道int类型的大小是4个字节,char类型的大小是个字节等等。这里同理可以计算指针类型变量的大小
//指针变量的大小取决于环境32位4,64位是8,我使用的是64位
return 0;
}
运行:
二:指针运算:
(一)指针加减整数:
指针加减整数会移动指针指向的位置,移动的位置大小取决于指针类型。
例如:char*p指向的是char类型的变量,char的大小是1字节,所以当p+1的时候跳过一个char类型变量的空间;同理int*p时p+1跳过一个int类型变量也就是4字节,其他类型同理。
看代码和注释:
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;//强制类型将int*转换成char*
int* pi = &n;
//d%p打印地址
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc + 1);
printf("%p\n", pi);
printf("%p\n", pi + 1);
return 0;
}
运行结果:
可以看到char*类型的指针变量跳过了一个字节,int*类型的跳过了4个字节
(二)指针相减
指针减指针得到的是两指针指向的地址之间的元素
代码示例看注释理解:
#include <stdio.h>
int main()
{
char* p = "abcdefghi";//p指向的是字符串的首元素地址,后面详细讲解
p = p + 5;//指针p现在指向的是字符串第五个字符后面的位置(指向f)
char* ptr = p;
p = p - 5;//将指针p重新指向初始位置a的地址
printf("%s\n%s\n", p, ptr);//现在是指针p和otr各自指向的位置打印的字符串
printf("%d\n", (ptr - p));//两之相减得到的结果
return 9;
}
两指针不能够相加,相加是非法的,可以自己试一试,会报错的。
三:const修饰指针:
const是限定修饰的值不可以修改。如const int num = 12;这里num = 0;这种操作编译器就会报错,自己可以试一试。但是使用指针可以绕过去修改cost修饰的变量的值。看下图:
不可修改。
#include <stdio.h>
int main()
{
const int n = 0;
printf("%d\n", n);
int* p = &n;
printf("%d\n", *p = 12);
return 0;
}
可以修改,原来的0被修改成了12。
(1)const在指针前面:
当我们试图修改num指向的值的时候,发现编译器报错了。
#include <stdio.h>
int main()
{
const short* num = 12;
printf("%hd\n", num);
printf("%p\n", num);//num被修改之前的地址
//*num = 0;这里是不被允许的
num = NULL;//将指针变量num的值置为空
printf("%p\n", num);//num被修改之后的地址
return 0;
}
所以:const修饰指针变量在*的前面的时候其对应地址可以修改,但是指针指向的内容不可以修改.
(2)const在*后面:
注意:因为上面的代码,可能有些同志会将代码写成这样:这样编译器不会报错,但是确实错的,因为const已经限制了num后面的值12,12是一个整型,然而前面的short*是指针变量,应该修饰的是指针,前后不对应,是错的。
还是刚刚的代码,我们发现在修改其地址的时候编译器是会报错的,所以不能够修改其地址,我们试一下能不能修改它的
int main()
{
short m = 12;
short* const num = &m;
printf("num修改之前的值为:%hd\n", *num);
*num = 0;
printf("num修改后的值为:%hd\n", *num);
//printf("%p\n", num);//num被修改之前的地址
//num = NULL;//将指针变量num的值置为空
//printf("%p\n", num);//num被修改之后的地址
return 0;
}
运行:
(3)*前后都有const时,地址和值都不可以修改。可以自己试一下。
四:野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的成因:
(1)未初始化指针:
#include <stdio.h>
int main()
{
int* ptr;
printf("%p\n", ptr);
return 0;
}
当我们执行上述代码的时候:
(2)指针越界访问:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
int* p = &arr[0];
for (i = 0; i <= 12; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
上述代码指针随着循环的进行,指向arr[10]后面就是随机的了,前面arr中的10个元素都是初始化为0的,指针指向的位置相当于是知道的,但是后面指向10个元素之后的地址是没有初始化的,由系统随机分配,根据指针的定义,指针指向的地址是随机的,那他就是野指针。这个代码i<=12程序会崩掉,但是i<=11就不会,可以自己去尝试一下。
(3)指针指向的空间被释放:
#include <stdio.h>
char* ret()
{
char ch = 10;
char* ptr = &ch;
return ptr;
}
int main()
{
char* ch = ret();
printf("%p", ch);
return 0;
}
上面代码每次得到的结果都不同:
得到的地址是随机的。
解释:因为ret()函数内局部变量的值和空间随着函数的执行完毕就会被销毁,所以返回的地址也是一个无效的,随机的地址,将它赋给ch,ch打印出来的就是随机的地址。
如何规避野指针:
(1)记得给指针初始化;
(2)注意指针的越界访问;
(3)注意指针指向的空间变化。
欢迎评论指正不对或者不恰当之处。