指针
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); }