目录
内存地址
- CPU内存的访问 ------> 通过内存地址来读写内存数据
- 读数据:CPU与内存条硬件之间有个地址总线,读数据的时候CPU通过地址总线将要访问的内存地址告诉内存条,内存条回数据给CPU,这样CPU就能获取内存里面的数据
- 写数据:CPU通过地址总线将地址告诉内存条,内存将数据存进去
- 对于CPU而言地址是唯一的读写内存的方式
- 内存地址是一个整数
- 应用程序使用的内存地址,其实不是真实的物理地址,而是操作系统映射好的虚拟的内存地址
- 32位系统与64位系统,虚拟地址是不一样的,32位系统它的地址是由32位的二进制表示的,64为的虚拟地址是由64位的二进制数据表示的
取变量地址
- C语言里面使用'&'来获取变量所在内存的地址
// 取地址
int a = 10;
// 每一个变量都会有一个内存地址,
// 我们怎么求的这个变量的内存地址呢?
// 使用取地址符号&,地址的显示都是16进制的
printf("0x%x\n", &a); // 获得这个变量的起始地址;
// end
- 32位程序内存地址是32位的,64位程序内存地址是64位的 ------> 和操作系统有关
指针
- C语言里面存放内存地址数据的变量就是指针,对于32位程序,那么指针变量的存储空间是4个字节,对于64位程序,指针变量的存储空间是8个字节
- 指针的实质 ------> 指针存放一个地址数据的变量,指针变量
- 指针的定义 ------> 数据类型* 变量名称,表示这个变量存放的是所定义的数据类型的内存地址数据,也确定了指向内存的大小
- 指针变量内存分配
- 指针变量定义在哪里,指针变量的内存就分配在哪里
- 比如是一个全局的指针变量,那么就将内存分配到我们的数据段,大小为4个字节(32Bit),如果指针变量定义为一个局部变量,那么指针变量的内存就分配到栈上
- 指针变量就是4个字节(32位)的变量,用来存放内存地址
// 32bit os
int m = 0; // 申请了一个内存,名字为m, 类型为int, 4个字节;
int* p = NULL; // 申请了一个内存,名字为p, 4个字节,用来存放内存地址,而且有这个变量的大小信息, int,
p = &m;
// end
// 任何一个指针变量,存放一个有效的内存地址,
// 或者是你知道的一个有意义的数据;
// NULL 0x0000000 地址,是操作系统禁止访问,空地址,空指针;
printf("mem size = %d\n", sizeof(p));
printf("mem size = %d\n", sizeof(int*));
// end
指针的使用
- 指针变量的初始化
- 如果指针变量暂时没有存放任何内存的地址的数据,那么一般我们会将它初始化为NULL, 例如int* p = NULL, NULL就是指针变量里面存放的是0
- 通过取地址符号获取变量的地址,然后赋值给指针变量;int a; int* p = &a
- 给指针变量存地址的时候一定要是有意义的地址,不能乱搞
- 通过指针读数据
- 取得指针变量里面存放的内存地址的数据
- cpu使用这个地址去访问内存,然后得到数据, 这两步通过 (*指针变量)来操作
// 使用指针来访问指针指向的内存的数据;
double n = 7; // 8个字节
//类型* 大小取决于这个类型
double* ptr = &n; // ptr 4个字节,存放了n的起始地址;
// 使用ptr来访问内存数据呢?内存的起始地址,大小
// *号来表示,从当前指针变量存放的内存地址开始,到当前指针数据类型的大小结束(double, 8, int 4)
// 使用指针,来访问我们的内存数据:
// 指针来访问内存数据两大要素:
// (1) 内存起始地址;
// (2) 内存的大小,所占的字节数;(指针的类型决定), sizeof(数据类型)
// 定义一个指针变量 类型*
printf("%lf\n", *ptr);
char* ch_ptr; // *ch_ptr 内存大小为sizeof(char)
short* sh_ptr; // *sh_ptr 内存大小是sizeof(short)
int* int_ptr; // *int_ptr 内存大小是sizeof(int)
// 普通的指针,存放地址的4个字节而已;
int* *int_ptr2; // *int_ptr2 内存大小sizeof(int*) 4个字节
// end
- 通过指针来写数据
- 取得指针变量里面存放的内存地址数据
- cpu使用这个地址,将数据写入到内存里面
- 使用指针访问内存的时候,要确保指针变量里面存放的地址是哪个内存是使用指针的关键
- 如果指针变量里面存放的地址是一个不可控的地址数据,那么操作系统会杀掉当前的进程,抛出非法错误,比如使用存放 NULL的内存地址数据的指针
char ch = 40;
ch_ptr = &ch;
sh_ptr = 789; // 本身是4个字节可以存放4个字节允许的数据;
// printf("%d\n", *sh_ptr); // 非法的异常, sh_ptr --> 789, 然后作为内存其实地址, 2个字节的内存大小;
// 789是一个被OS保护的地址,
// 存放内存地址而用;
void* v_ptr = &ch; // ch变量的内存地址
// printf(*v_ptr); // 编译错误,void类型,没有字节大小
// end
指针参数
- 指针作为函数参数:将一个内存地址传入,函数里面就能拿到这个内存地址,通过内存地址来访问变量的内存
- 编写函数,交换两个变量的值,分析下,指针作为参数传递的全过程
int lhs = 7;
int rhs = 8;
swap(&lhs, &rhs);
printf("lhs = %d rhs = %d\n", lhs, rhs);
// 把一块内存的起始地址传递过去;
static void swap(int* a, int* b) {
// *a = 7, *b = 8
int temp = *a; // temp = 7;
*a = *b;
*b = temp;
}
代码练习
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 把一块内存的起始地址传递过去;
static void swap(int* a, int* b) {
// *a = 7, *b = 8
int temp = *a; // temp = 7;
*a = *b;
*b = temp;
}
int main(int argc, char** argv) {
// 取地址
int a = 10;
// 每一个变量都会有一个内存地址,
// 我们怎么求的这个变量的内存地址呢?
// 使用取地址符号&,地址的显示都是16进制的
printf("0x%x\n", &a); // 获得这个变量的起始地址;
// end
// 指针
// (1)指针是一个变量, 变量定义在哪里内存分配在哪里;
// (2)指针是一个什么样的变量呢?是存放内存地址的变量;
// (3)变量内存多大呢?内存地址有多大,32位OS-->(4个字节), 64位OS(8个字节)
// (4)变量存放的是哪种数据类型的类型的地址;
// 类型* 变量名称;
// int* 变量int类型的变量内存地址;
// char* 变量char类型变量内存地址;
// void* 没有任何数据类型的,就是一个地址,没有变量的大小信息的;
// 32bit os
int m = 0; // 申请了一个内存,名字为m, 类型为int, 4个字节;
int* p = NULL; // 申请了一个内存,名字为p, 4个字节,用来存放内存地址,而且有这个变量的大小信息, int,
p = &m;
// end
// 任何一个指针变量,存放一个有效的内存地址,
// 或者是你知道的一个有意义的数据;
// NULL 0x0000000 地址,是操作系统禁止访问,空地址,空指针;
printf("mem size = %d\n", sizeof(p));
printf("mem size = %d\n", sizeof(int*));
// end
// 使用指针来访问指针指向的内存的数据;
double n = 7; // 8个字节
//类型* 大小取决于这个类型
double* ptr = &n; // ptr 4个字节,存放了n的起始地址;
// 使用ptr来访问内存数据呢?内存的起始地址,大小
// *号来表示,从当前指针变量存放的内存地址开始,到当前指针数据类型的大小结束(double, 8, int 4)
// 使用指针,来访问我们的内存数据:
// 指针来访问内存数据两大要素:
// (1) 内存起始地址;
// (2) 内存的大小,所占的字节数;(指针的类型决定), sizeof(数据类型)
// 定义一个指针变量 类型*
printf("%lf\n", *ptr);
char* ch_ptr; // *ch_ptr 内存大小为sizeof(char)
short* sh_ptr; // *sh_ptr 内存大小是sizeof(short)
int* int_ptr; // *int_ptr 内存大小是sizeof(int)
// 普通的指针,存放地址的4个字节而已;
int* *int_ptr2; // *int_ptr2 内存大小sizeof(int*) 4个字节
// end
char ch = 40;
ch_ptr = &ch;
sh_ptr = 789; // 本身是4个字节可以存放4个字节允许的数据;
// printf("%d\n", *sh_ptr); // 非法的异常, sh_ptr --> 789, 然后作为内存其实地址, 2个字节的内存大小;
// 789是一个被OS保护的地址,
// 存放内存地址而用;
void* v_ptr = &ch; // ch变量的内存地址
// printf(*v_ptr); // 编译错误,void类型,没有字节大小
// end
int lhs = 7;
int rhs = 8;
swap(&lhs, &rhs);
printf("lhs = %d rhs = %d\n", lhs, rhs);
system("pause");
return 0;
}