指针

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

 

posted @ 2019-08-04 11:15  Coding_Changes_LIfe  阅读(151)  评论(0编辑  收藏  举报