指针
1、内存载入过程:
(1)程序要进行的操作对应的代码被装载到代码区。
(2)全局和静态数据等装载到数据区
(3)开辟堆栈,供临变量等使用
可见,内存中的数据是多种多样的,既可以是操作,也可以是数据,都被存储在一个个的内存小格子中,每个小格子存储8个二进制位。
变量是对程序中数据存储空间的抽象
指针:一个变量的地址
指针变量:专门存放变量地址的变量叫~
变量是指其值可以变化的量。计算机中,指令代码、数据都存储于内存中。变量也需要存储在内存中。在计算机中,每个变量都被分配了一块内存空间,在这些空间里存储的就是变量的值。变量之所以可以变化,就是这个存储空间可以存储不同的数值。存储空间里的值变化,则变量对应的值也变化。同一个时间,内存空间里只能保存一份值,新值冲掉了原来的旧值。每个内存单元都有编号,这些是内存的地址。
#include <stdio.h>
// 内存地址是内存当中存储数据的一个标识,并不是数据本身,通过内存地址可以找到内存当中存储的数据。
// *是根据地址取出内容, &是取地址
// 一个程序载入内存中,代码和数据都有地址,函数就是代码,变量就是数据;代码查看要回汇编,而数据可以直接查看
//外挂就是调用函数,修改数据
//内存容量比较小,读写速度比较快,但断电就没有了,cpu不能直接访问硬盘的数据
//实际上内存是把8个8个bit排成1组, 每1组成为1个单位, 大小是1byte(字节), cpu每一次只能访问1个byte
//而不能单独去访问具体的1个小格子(bit). 1个byte字节就是内存的最小的IO单位.
// 内存就是一栋大楼, 而内存里每1个字节就是大楼的每个房间, 而内存地址就是房间的门牌号码了.
//32位的计算机,在32位操作系统中, 内存的地址就是32位的2进制数, 那么2^32到底是多少个? 2^32 = 4 * 1024(G) * 1024(M) * 1024(K) = 4294967296 , 就是4G 啊, 而每1个地址对应1个1个字节,
// 容量就是1byte, 所以2^32个地址就总共能对应应4GB 的内存容量啊, 这里的B指的是byte 字节啊。
// 其地址空间为32位,采用32位地址编码,一个小房间就是4个字节,32位
//为什么32位的计算机只支持4G内存?
// 4G=4*1024M = 4*1024*1024KB =4*1024*1024*1024 byte = 2^32 byte =4GB
// 0000 0000 0000 0000 0000 0000 0000 00000
// 00 00 00 00
// 1111 1111 1111 1111 1111 1111 1111 1111
// ff ff ff ff
//注意:内存地址不等于内存,一个内存地址对应一个字节大小的内存
void main1(){
printf("%x \n",main1); //函数名就是函数代码块的首地址
int n =4; //变量就是对内存空间的数据的抽象
printf("%x \n",&n); //&n是一个变量的地址,也是一个指针,指向num
printf("%d \n",*(&n)); // *是根据地址取出内容
int num =100;
int *p = # //P是一个指针变量,可以指向任何变量的地址 定义时:*p前面有类型限制 ,引用时:*p没有类型限制
// 用num访问100是直接访问,用*p访问是间接访问;
//p只是存储 num 变量的起始地址,前面的int确定它往后读取多长,也就是读取4个字节,按照int类型解析数据
// 定义时, (int *) 是一个指向int类型的指针变量,容纳 int 变量的地址
//所谓指针,指的是“储存的内容是地址的量”,两个要点:一、指针是个量,对应着一块内存区域,二,指针存储的信息是某个内存单元的地址
// 指针是地址,地址不是指针。
//地址0x567DFd只是一个纯粹的地址,而指针是一个可以存储地址的变量,它的值可以变化,而且有int类型
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void changeNum(int *p){ //定义一个int类型的指针变量,用于接收变量的地址
*p = 111; //根据地址改变其数据
}
void main2(){
int num=100;
int *p = # //定义指针时必须初始化, p是变量,而&num是常量
// p = num;
// 编译时:此句就相当于把100当做一个16进制的地址赋值给指针p,也就是p指向的内存地址为 0X00000064 (100的16进制)地址空间,此为系统空间,运行时:会发生访问冲突
changeNum(&num); // 等价于 changeNum(p); C语言要改变外部变量只能传地址,java和c++可以用引用传值,c语言没有引用
printf("%d\n",num);
num=100;
// p num p = # 把num的地址编号赋给了p所指向的内存空间 value:61fe4c,p自己的内存空间也有编号 addr:61fe40
// addr (&): 61fe40 61fe4c
// value (*): 61fe4c 0x64(100)
printf("num=%x\n",num); // 变量是对程序中数据存储空间的抽象 num指向的值为0x64
printf("&num=%x\n",&num); // & 取出num的地址编号 61fe4c
printf("p=%x\n",p); // p是一个指针变量,存储的是num的地址编号 61fe4c
printf("*p=%x\n",*p); // * 表示取出 指针 p 地址 61fe4c 所指向的值 0x64
printf("&(*p)=%x\n",&(*p)); // *p是0x64,取64的地址
printf("&p=%x\n",&p); // &取出指针 p 的地址编号61fe40,而指针 p 地址中的值又是 num 变量内存地址(编号),61fe4c。
printf("*(&p)=%x\n",*(&p)); // &取出指针 p 的地址61fe40 ,再取出此地址61fe40中内容p,即: *(&p) = 61fe4c =&num =p
printf("p=%p\n",p); // 按地址打印,0x0061fe4c
// 结论: &和* 两个在一起,会相互抵消其作用
/*
num=0x64
&num=61fe4c
p=61fe4c
*p=0x64
&(*p)=61fe4c
&p=61fe40
*(&p)=61fe4c
p=0x0061fe4c
*/
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//数组作为函数参数时,传递的是指针,会改变原来的数组内容,因为数组拷贝非常浪费内存
//除数组以外其他参数的传递都是副本机制,新建一个变量的副本
//改变外部变量
//函数参数如果是一个数据,传递的是数据的地址(指针)
//函数参数如果是一个指针,传递的是指针的地址(指针的指针,二级指针)
void go(int a[5]){
printf("%d\n", sizeof(a)); //8 传过来的是数组a的地址,大小固定,8个字节
a[0] =9;
printf("a[0] in go :%d\n",a[0]);
}
void goPointer(int *p){ //从main函数传进来的p1和p不是同一个指针,新建一个指针变量p,存入p1的副本地址,即p=p1,
printf("p address in goPointer before :%x\n",p);//61fe44
int num =10;
p = # //改变p的指向,不影响p1的指向,还是61fe44
printf("p address in goPointer after :%x\n",p);//p address in goPointer after :61fdec
}
void changePointer(double **pp, int num ){ //从main函数传进来的pp是二级指针的地址,也就是一级指针内存编号
printf("*pp address in changePointer before :%x\n",*pp);//
*pp = # //改变*pp的指向,也就是改变一级指针的内存地址编号
printf("*pp address in changePointer after :%x\n",*pp);//p address in goPointer after :61fdec
}
void main(){
int num =100;
int *p1 =# //定义了int类型的指针,虽然类型变量是int类型,但指针存放num变量的地址,固定为8个字节
double dNum =10;
double *p2 =&dNum; //定义了一个一级指针p2,虽然类型是double基本数据类型,但指针存放dNum变量的地址,所以固定是8个字节
double *p3 =&p2; //错误,p1内存地址编号32位的平台是固定4个字节的长度,直接把地址赋值给double类型8个字节的指针会报错 //initialization of 'double *' from incompatible pointer type 'int **'
double **pp = &p2; //等价于(double *) *pp = &p2, pp指向了一个double * 类型的变量,存储的是一个double类型的一级指针(地址),也就是&p2
// 二级指针 => 一级指针 => 基本数据类型
// 定义时: int **pp => int *p => int pp
// 引用时: pp => *p => **pp
float *p4; //float类型的指针
printf("%d,%d,%d,%d,%d,%d\n", sizeof(p1), sizeof(p2), sizeof(p3), sizeof(p4), sizeof(double *),sizeof(int *)); // output 8,8,8,8,8,8
//指针只是一个存储指针的变量,大小固定是8个字节,与类型无关,与平台有关,32位的平台是4个字节
int a[5] ={1,2,3,4,5};
printf("%d\n", sizeof(a)); // 20
go(a); //数组名代表数组的首地址
printf("a[0] in main :%d\n",a[0]); //9
goPointer(p1); //p1的地址传给了goPointer函数
printf("p1 address in main :%x\n",p1);//p1 address in main :61fe44
//测试二维指针的函数传递
int n1 =20, n2=30;
printf("*pp = %x, p2 = %x\n",*pp,p2); //*pp = 61fe20, p2 = 61fe20 *p就是p2的内存地址编号,所以两者一样, 一级指针
changePointer(pp,n1); //*pp address in changePointer before :61fe20
//*pp address in changePointer after :61fec8
printf(" *pp address in main :%x\n",*pp);// pp address in main :61fec8 change之后把二级指针所指向的值,也就是一级指针的内存编号改变了 20 => c8
changePointer(&p2,n2); //*pp address in changePointer before :61fdc8 &p2,就相当于pp的存储内容,也就是一级指针的内存编号
//*pp address in changePointer after :61fdc8
printf(" p2 address in main :%x\n",p2);//p2 address in main :61fdc8
}

1、指针存储的是变量的首地址,指明了从哪里开始,从哪里结束由类型来指定,类型指定长度和如何解析数据,在32位系统中是用4个字节存储指针地址,不用与指针所指向的值混淆。
2、指针运算
一级指针
1、函数改变外部变量的时候,需要用到指针,传入某个值的地址(间接修改)
2、跨进程改变变量(外挂)
3、数组会直接改变其值
4、可以存储数组的首地址,数组下标法遍历(可能会越界),指针法遍历
5、作为函数返回值,返回地址,但一定不能返回指向栈的地址
6、一级指针指向字符串的首地址,间接访问结构体,共用体
7、创建堆上的动态数组,访问数组中的数据
#include <stdio.h>
#define getName(x) #x //#x意思是把getName(x)替换成 "x" 字符串
#define mutiply(x) (x)*(x) //宏定义函数
#define add(x,y) (x)*(y) //宏定义函数
void main(){
//传递main的地址,打印函数名,可以使用宏定义
printf("%s\n",getName(main)); //main
printf("%d\n",mutiply(2)); //4
printf("%d\n",add(2,3)); //6
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// * 和 ++ 优先级深究
void main7(){
int a[3] = {1,4,9};
int *pa = a; //*p等价于int类型,p等价于int *类型 指针要在定义的时候初始化
printf("%d, %p\n",*pa,pa); //1, 0061FEC0
*pa++; //说明++的优先级比*高
printf("%d, %p\n",*pa,pa); //4, 0061FEC4
int b[3] = {1,4,9};
int *pb = a;
printf("%d, %p\n",*pb,pb); //1, 0061FEBC
++*pb; //++要与pb接触才有优先级,实际上是 ++(*p)
printf("%d, %p\n",*pb,pb); // 2, 0061FEBC
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
swap(int *p1, int *p2) //这里会新建一个p1,p2的指针变量存储pointer_1和pointer_2的地址,交换了p1和p2的地址后,和pointer_1,pointer_2没有关系
{ int *p; //p在定义的时候没有初始化,可能会出现垃圾数据
p=p1;
p1=p2;
p2=p;
}
void main()
{ int a,b;
int *pointer_1,*pointer_2;
scanf("%d,%d",&a,&b);
pointer_1=&a; pointer_2=&b;
if(a<b) swap(pointer_1,pointer_2); //swap只是交换新建变量p1和p2的地址,和pointer_1,pointer_2没有关系
printf("%d,%d",*pointer_1,*pointer_2);
}

浙公网安备 33010602011771号