指针
一 有关内存的事
1.1 内存的作用
- 内存是沟通cpu和硬盘的桥梁;
- 暂时存放cpu运算的数据;
- 暂时存放与硬盘等外部存储器交换的数据
1.2 有关内存的两个概念
- 物理内存:实实在在的存储设备:内存
- 虚拟内存:操作系统虚拟出来的内存
- 操作系统会在物理内存和虚拟内存之间做映射
- 在32位系统下,每个进程的寻址范围是4G:0x00 00 00 00 - 0xff ff ff ff
- 写应用程序看到的都是虚拟内存地址
- 在运行程序的时候,操作系统会将虚拟内存进行分区:
1. 堆:在动态申请内存的时候,在堆里开辟内存
2. 栈:主要存放局部变量(在函数内部或复合语句中定义的变量)
3. 静态全局区:
a. 未初始化的静态全局区:没有初始化的静态变量(定义的时候前面加static修饰);或没有初始化的全局变量
b. 初始化的静态全局区:初始化了的静态变量;或初始化了的全局变量
4. 代码区:存放咱们的存放代码
5. 文字常量区:存放常量的
- 内存以字节为单位来存储数据的,可以将程序中的虚拟寻址空间看出一个很大的一维字符数组
二 指针的相关概念
- 操作系统给每个存储单元分配一个编号,从0x00 00 00 00 - 0xFF FF FF FF
- 这个编号称为地址
- 指针就是地址
- 指针变量:是个变量;这个变量用来存放一个地址编号
- 在32位平台下,地址总线是32位的,所以地址也是32位的编号,所以指针变量是32位即4字节
- 在64位平台下,地址总线是64位的,所以地址也是64位的编号,所以指针变量是64位即8字节
三 指针的定义方法
3.1 定义指针变量的步骤:
- 用*修饰指针变量名
- 指针变量要保存什么类型的变量的地址,就用该类型定义一个普通变量
- 从上往下整体替换
例如定义一个指针变量a来保存int类型变量的地址:
1. 用*修饰指针变量名a : *a
2. 定义一个int类型的普通变量temp: int temp
3. 从上到下整体替换: int *a
3.2 定义指针变量的一般步骤:
- 前提:必须明确该指针变量要保存什么类型变量的地址,该变量要有一个合法的内存空间
- 用三步法定义一个指针变量
- 建立目标变量和指针变量的关系
int main(int arg, char *argv[])
{
//第一步
//num 拥有一个合法内存空间
int num=10;
//第二步
//需求:定义一个指针变量,来存放num的地址
//p就是指针变量,变量名为p,而不是*p
//在定义的时候: *修饰p,表示p为指针变量
int *p;
//第三步
//建立p和num的关系:p保存num的地址
p = #
}
四 指针变量的使用(通过指针变量对保存的地址空间进行读写操作)
在指针变量的使用中:
int *p;
int num;
p=#
*p: 表示取指针变量p所保存的地址编号 对应的空间内容;
使用中用*p是指针变量p的解引用;
*p等价于num;
四 指针变量的类型
指针变量的自身类型和指针变量所指向的类型都是在指针变量的定义时决定的
4.1 指针变量的自身类型
将指针变量拖黑,剩下的是什么类型,指针变量自身就是什么类型
int *p;
剩下int *,所以指针变量p自身的类型是一个int型的指针
4.2 指针变量所指向的类型
将指针变量和离他最近的一个*一起拖黑,剩下的类型,就是指针变量指向的类型
int *p;
剩下int,所以指针变量p指向的类型是int型
4.3 指针变量的宽度
- 指针变量的宽度是指针变量的取值宽度(字节);
- 指针变量的宽度是由指针变量指向的类型的长度决定的;
4.4 指针变量的跨度
- 指针变量的跨度是指针变量+1时移动几个字节;
- 指针变量的跨度是由指针变量指向的类型的长度决定的;
4.5 指针变量的强制类型转换
- 如果跨度和宽度不一样时,就需要用到类型转换;
- 如何选择指针变量的类型,原则是跨度和宽度中最小的,例如跨度是1字节,宽度是2字节,就定义一个一字节的char *型指针变量
- 经过了强制类型转换后:星号边上是强制类型转换的部分是控制取值宽度的;有加号的是控制跨度的。
五 指针变量初始化
- 如果局部指针变量不初始化,保存的是随机的地址编号,千万别取值
- 指针变量初始化为NULL,也就是(void *)0地址,也是内存的起始地址,是受系统保护的
- 不能给指针变量赋普通数值
int *p=100;
此时100是地址编号100, *p表示在地址编号100取值,而地址编号100是未申请的不合法空间
-
指针变量不要操作越界的空间
-
指针变量初始化为合法空间
六 &取地址符号和*指针解引用符号的区别
int num=10;
int *p;
- num的类型为int型
- 如果对一个变量取地址,整个表达式的类型是变量的类型加*
&num的类型为int *类型
- p的类型为int *类型
- 如果对一个指针变量取*,整个表达式的类型是指针变量的类型减*
*p的类型为int类型
5. 总结上面的内容:如果*和&同时存在(不管谁在前面),可以相互抵消(多个*和&时,从右往左抵消)
用上面总结的理论来证明*p=num:
1. p=&num, 所以等号两边可以都加一个*号得到下面表达式
2. *p=*&num, 将num前的一对*&抵消掉,就得到下面表达式
3. *p=num
用上面理论化简:
七 指针的注意事项
- void不能定义变量,因为在定义变量时要给变量开辟内存空间,而void不知道空间大小
void a;
- void *可以定义变量,因为void * 指针类型在32位平台分配4字节,在64位平台分配8字节;但不能用 * 号解引用该变量,因为该指针变量指向的变量的类型为void类型,void类型无法确定取值宽度,但可以用类型转换将该指针变量临时强转成其他类型指针
- 用void *定义的指针变量称为万能指针,他能指向任意类型的一级指针;但不能直接用 * 号解引用,要先做强转类型转换后再解引用
[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int arg, char *argv[])
{
void *p;
int data1=10;
char data2='A';
float data3=0.1f;
p=&data3;
p=&data2;
p=&data1;
printf("%d\n",*(int *)p);
}
[root@ansible9 ~]# ./a.out
10