对于学C的人来说,指针应该是很多人噩梦的开始。刚开始时可能还能听懂,到后面就什么不会了。指针是后面学习的基础,所以学好指针十分关键。
一.内存、地址、指针
地址指的就是内存中某一单元的编号。就像楼房的门牌号一样,可以通过门牌号定位到某个房间,我们可以通过地址定位到内存的一个地方。指针是什么呢?指针可以理解成地址,通过指针也可以定位到内存中的某一个单元。指针的作用就是访问内存。
二.指针变量
1.取地址符(&)
取地址符是&,可不要与&&弄混。取地址符,顾名思义,就是取出地址的符号。如我们定义了一个int变量a,然后取地址a,(&a),这样我们就可以得到a在内存上所占4个字节空间的第一个字节的地址了(int变量占4个字节)。
2.指针变量定义
指针变量的定义格式:
int a=10;
int* p=&a;
类型+*+变量名
指针变量存储的是地址,像上面的代码,p存储的是a在内存上的地址。
注意:指针变量本身也是一个变量,也会在内存上开辟空间来储存地址,因此指针变量也有地址,这就有了指向指针的指针,就是再定义一个指针变量来储存指针变量p的地址。
3.解引用操作符(*)
定义完之后,我们可以使用解引用符来将解引用指针变量,取出它所储存的地址里的值。
int a=10;
int* p=&a;
int c=*p //解引用p后,c就等于10
4.指针变量的大小
char* 、int* 、double* 、short*等,在相同的环境下所有的指针变量的大小是相等的,在32位是4个字节,在64位是8个字节。
5.指针变量类型的意义
char*类型的指针变量每次可访问1个字节,short*类型的指针变量每次可访问2个字节,int*类型的指针变量每次可访问4个字节。
由此我们可以进行下面的内容,指针变量加减整数。
p+1(p是int*类型)就是在一开始的地址往后跳4个字节,相同的是如果是char*那么就往后跳1个字节。
6.void*类型
void*是一种特殊的指针变量类型,没有具体的类型,又叫泛型指针,可以接受任何类型变量的地址。当然,它的缺点也很明显,不能解引用,不能进行与整数相加减,无法进行指针的运算。
三.野指针
野指针就是指向位置不可知的指针。
1.成因
(1).指针未初始化,让指针随机指向了内存空间的某一地址;
(2).指针越界访问,比如我们定义了一个数组有10个变量,结果我们操作后让指针指向了第11个数,这时就会发生指针的越界访问,非法访问内存;
(3).指针指向的空间释放,这种问题一般出现在有函数调用的代码里,在前面的文章中有介绍函数栈帧的创建和销毁(如果大家感兴趣的话可以去看一下),可知,在函数调用完之后,调用函数开辟的空间会被释放,如果我们返回了函数内定义的一个变量的地址,那么空间释放后该空间的内容早已不是储存时的内容,因此会非法访问内存。
2.规避
预防野指针的出现,我们一定用注意好上面的3个原因。如果定义一个指针变量,可以让其先指向空,即NULL。
四.assert断言
在<assert.h>的头文件里定义了一个函数assert(),用以用以确保程序符合某一条件,如果不符合就终止运行,同时返回出错的文件名和代码的行号。如果程序不再需要assert无需一个一个全部删掉,只需在<assert.h>的头文件前加上#define NDEBUG就可关掉全部assert,十分方便。当然缺点也有,增加程序的运行时间。
五.传址调用
顾名思义,就是传入地址以便使用。
void swapp(int* x,int* y)
{
int t;
t = *x;
*x = *y;
*y = t;
}
int main()
{
int a=1, b=2;
int* pa = &a;
int* pb = &b;
swapp(pa,pb);
printf("%d %d", a, b);
return 0;
}
互换值就是经典的传址调用的例子。
如果我们只是简单传入a和b的值,函数调用传入的值只是在空间上赋值了a和b的值,形参的改变不会影响实参的变化。但是如果我们传入的是地址,那么直接取地址就是在原来a和b的内存空间单元上修改,因此可以完成值交换的功能。
如果我们编写程序时要对实参做一些操作,要选择传址调用。
以上就是全部内容了,创造不易,如果喜欢的话点个赞点个关注^-^
谢谢大家^_^