基本概念:
什么是内存
把内存想象成一个很长的大走廊
走廊上有很多房间,每个房间的大小是 1 byte
每个房间都有一个房间编号,编号从 0 开始算,依次累加(看到的房间号一般是比较大的数字),房间号就称为"地址".
指针也是一个变量,变量里存的就是一个整数,这个整数具有特定的含义,就是内存中的一个地址.
内存的特点:
1.内存支持随机访问. CPU在访问内存的时候,访问任何一个地址的数据的代价都非常低.
int arr[1亿];访问 arr[0]和访问arr[1亿 - 1]效率是一样的
2.内存和外存.
a)访问速度:访问内存 ns 速度快,访问外存 um 速度慢(慢3 - 4个数量级)
b)容量:内存的容量小,外存容量大
c)成本:内存成本高,外存成本低
d)断电:内存断电后数据就没了,外存断电数据还在
可以创建一个变量 p ,用来保存 num 的地址.此时 p 这个变量就是指针变量.
p 的类型叫做 int* ,指针是一个统称.int* ,char* ,double* 都算指针.
#include <stdio.h>
#include <stdlib.h>
int main(){
int num = 10;//变量的创建过程就是对内存的申请
int* p = #
//下面括号里的 * 是间接访问操作符/解引用运算
printf("%d\n",*p);
system("pause");
return 0;
}
内存必须先申请才能使用,没申请不能用.如果强行使用未申请的内存就会出现访问非法内存的情况,这这是一种常见的"未定义行为".
C/C++要着重注意这种情况,Java中如果访问非法内存JVM都能检测出来,通过抛出异常及时通知.
包含一个非法地址的指针,叫做"野指针",对野指针解引用是非常危险的.
指针变量涉及两个要素: int* char*
1.指针对应的内存是哪个地址 (指针的值体现)
2.这个内存地址对应的内存大小是多大 (指针的类型体现)
有一种特殊的指针,只有地址,而没有内存对应的大小.
void* 正因为没有限制内存的大小, void* 对应的内存可以是任意大小.
void* 不能解引用.
void* 用处很大.当某个函数不需要关注类型,或者需要多种指针类型的时候,就可以使用void* (memset是典型的例子)
void* 相当于是把类型完全交给程序员来控制和保证.
空指针 VS void*
空指针:比较特殊的指针,空指针的地址就是 0 .NULL
空指针是一种特殊的野指针.操作系统的内核可以用到,别的时候调用不了
操作系统是一个软件,用来管理硬件设备和各种软件资源的.
一个操作系统 = 内核 + 配套的应用系统
内核是核心所在,软硬件的管理主要都是内核完成的.
平时写的程序都是应用程序,跑在系统之上.还有一些特殊的程序是跑在内核中的(驱动程序(显卡驱动/鼠标驱动)).
32位系统上,一个指针占用4个字节.支持的最大内存就是 4GB
4个字节,能表达的最大数字是多少? 42亿 => 4GB
64位系统上,指针是占 8 个字节.
64位系统能运行32位程序的.VS生成的程序默认是按照32位来生成.
C语言中煤种类型占几个字节,取决于具体的编译器.
C语言标准中只是规定了 int/long 不能低于4个字节,具体是4 还是8,就和编译器相关了,也和32位/ 64位系统是相关的.
NULL 是一个典型的非法内存,如果解引用一定会出现问题.
#include <stdio.h>
#include <stdlib.h>
int main(){
int num = 0x11223344;
int* p = #
char* p2 = (char*)p;
printf("%x\n", *p2);
system("pause");
return 0;
}
11 22 33 44 大端字节序
44 33 22 11 小端字节序
0xcc这个数字对应到 int3 指令(CPU的指令),遇到这个指令程序就会直接崩溃.
指针指向的空间释放
有一个出场率很高的笔试题:
#include <stdio.h>
#include <stdlib.h>
int* Func(){
int num = 10;
return #
}
int main(){
int* p = Func();
printf("%d\n",*p);
system("pause");
return 0;
}
虽然输出结果为10 但是是有问题的:因为局部变量的生命周期在函数运行结束之后就结束了.申请的num开辟的内存也就释放掉了.之所以能够输出10是恰好这里的内存还没有被其他程序占用.
如何规避野指针:
1.指针初始化
2.小心指针越界
3.指针指向空间释放即置NULL
4.指针使用之前检查有效性