由于指针知识较多并且较为重要,所以分几章阐述。
就比如我们学校的一栋宿舍楼,有n个房间,每个房间我们可以理解成一个内存单元,一内存单元就是一字节,然后一个房间可以住八个人,也就是8bit。那么我们要进去找人的话,我们只能从一楼往上一个一个找,效率极其低下。但是我们要是给每个房间都编号那么我们就可以直接找到那个人待的房间,这就是内存指针的一种比喻。
#include <stdio.h>
int main()
{
int a = 10;
&a;//取出a的地址
printf("%p\n", &a);
return 0;
}
&a取地址操作符取的是字节最小的那个地址,但是这不影响,我们找到了第一个字节,后面几个字节就可以顺藤摸瓜的找出来了。
指针变量:我们既然取出来了地址,那么肯定得保存下来方面后面使用,那我们怎么保存呢,这时候就用到了指针变量。指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
#include <stdio.h>
int main()
{
int a = 10;
int * pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
指针变量也是变量,是用来存放地址的,只要在指针变量里的值都被认为是地址。我们看的出来pa的前面是int *,这里的*是指pa变量是指针变量,int指pa指向的值是int类型的对象。
解引用操作符:*a
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;
return 0;
}
当我们拿到了地址就可以由地址(指针)找到指针指向的对象。上面第六行代码*pa意思是通过pa里存放的地址找到pa指向的对象,也就是a。当然肯定会有很多人跟我有一样的疑问:我们之前想修改一个变量的值都是直接a=0,这样不会更方便吗,这里我也不太清楚,待学到后面再来解惑,能知道的就是多了一种途径去修改变量。
指针变量大小:上面说了32位机器会有32根地址线,每根线会发出电信号转为数字信号‘1’,‘0’。那么我们把32根地址线发出的二进制序列当作地址,也就是一个地址是32bit,也就是4字节。而指针变量是用来保存地址的,所以在32位机器里,指针变量大小是4字节,64位机器里指针变量是8字节。
指针的解引用:指针的类型决定了对指针解引用时的权限(一次能操作多少的字节)。char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
指针+-整数:
由此可以看出字符串指针变量加1地址加1字节,整形加1,地址加4字节,所以指针类型决定了指针变量向前或向后一步能走多远。
int * p;//没有const修饰?
int const * p;//const 放在*的左边做修饰
int * const p;//const 放在*的右边做修饰
const放在*左边修饰时,修饰的是指针变量p指向的对象,指向的对象不可以被改变,但p本身可以被改变。
const放在*右边修饰时,修饰的是指针变量p本身,保证了指针变量p本身无法被修改,但p指向的内容可以被修改。
指针运算:1.指针+-整数 2.指针-指针 3.指针的关系运算
1.指针+-整数:指针加减整数前面提过了。
2.指针-指针:指针减指针的结果的绝对值就是两个指针之间差的元素个数(而不是比特数)。特别的是,当我们用到指针减指针是需要确保两个前提条件,第一点是两个指针必须指向同一个连续的内存区域,第二点是类型需要相同。只有满足了以上两点前提条件,指针减指针才能是有意义的。
3.指针的关系运算:例如:p(指针变量)<arr(数组)。
野指针:
野指针就是指针指向的位置是不知道的(随机的,不正确的,没有明确限制的)。野指针有很大的危害:内存泄漏,程序崩溃,安全漏洞,数据损坏。
野指针出现的三大原因:1.指针未初始化:指针未初始化的话系统会随机赋值。2.指针越界访问:当指针指向的范围超出了操作系统所给范围,它就是野指针。3.指针指向的空间被释放。
避免野指针:如果知道指针要指哪块空间要明确初始,不知道的话就直接给指针赋值NULL。初始化如下:
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
小心指针越界:一个程序向内存申请了哪些内存指针就只能访问那些空间,不能超出范围,不然就是越界。
#define NDEBUG
#include <assert.h>
再重新启动程序,程序就会禁用所有assert()语句。当我们又要使用的时候,只要把宏NDEBUG注释掉就行。
当然assert()也是有缺点的,它的缺点是引入了额外的检查,增加了程序运行时间。一般我们会在Debug版本中使用,在release版本中选择禁用即可。在Debug中使用有利于程序员排查错误,在release中被优化掉,不影响用户运行效率。