认识指针

指针的定义是一种保存变量地址的变量。这篇文章主要是清楚“指针保存变量地址”这个认知。

首先认识两个符号:*是间接寻址或间接引用运算符。 &用来取一个对象的地址。

常见指针用法如下:

        int *p;      
        int x = 1;                
        p = &x;
        printf("p = %x, &x = %x\n", p, &x);
        printf("*p = %d, x = %d\n", *p, x);
对指针内部布局有一个认识:


首先明确几个概念:

指针变量本身起始地址未知,指针变量里存储的是内存地址。不管指针变量里存的是任何数据,都被当做地址来处理。

*前面的数据类型,只是说明指针所指向的内存里存储的数据类型。但在指针增值时,用处颇大。

不管什么样的指针类型,其大小都为4byte。(当然,这个与操作系统位数等有关)

认识一下,指针操作的每一步。

声明后,未进行任何操作:

        int *p;
        printf("&p = %x,p = %x, *p = %d\n", &p, p, *p);
       // &p = 0022ff44,p = 768d9e34, *p = -7494423
        //此时,p是一个野指针,其值是系统任意赋的。p和*p都是没有实际意义的。


指针赋值为空:

        p = (int *)NULL; //NULL的值为0 
        printf("&p = %x,p = %x\n", &p, p);
        // &p = 22ff44,p = 0 , *p没有意义 
这里,因为系统分配给每个程序一定的内存空间,程序不可访问未分配给自己的内存。


P指向一个有意义的数值:

        int a = 2;
        p = &a;
        *p = NULL; 
        printf("p = %x,&a = %x, *p = %d, a = %d\n", p, &a, *p, a);
        // p = 0022ff40, &a = 0022ff40, *p = 0, a = 0         
        
        *p = 8;
        printf("p = %x,&a = %x, *p = %d, a = %d\n", p, &a, *p, a); 
        // p = 0022ff40, &a = 0022ff40, *p = 8, a = 8

将数据存储到指定的内存地址(略显暴力):

	//int a = 1;
        //printf("&a = %x\n", &a);
        p = (int *)0x22ff40;   //p指向地址 0x22ff40
        *((int *)2293568) = 10;  //在该内存上填值 
        printf("p = %x, *P = %d\n", p, *p);
        // p = 22ff40, *p = 10
这里先打印出a的地址,为0x22ff40,表明这个地址可用,再赋值给指针p。

特殊情况下:

	p = (int *)&p;
        printf("&p = %d, p = %d, *P = %d\n", &p, p, *p);
        *p = 0x22ff40;
        printf("&p = %d, p = %d, *P = %d\n", &p, p, *p);        
        *p = 0;
        printf("&p = %d, p = %d, *P = %d\n", &p, p, *p);   
        /*
        &p = 2293572, p = 2293572, *P = 2293572
        &p = 2293572, p = 2293568, *p = 16      //改变*p的值,实际改变的是p保存的地址值
        &p = 2293572, p = 229356, *p = 0        //p的值已定,再改变*p,则改的就是指向的值,此时*p = 0. 
        */

malloc和free的注意事项:

        p = (int *)malloc(sizeof(int) * 5); 
        if(p == NULL)  //需要检查p是否为空,因为系统可能无法分配那么多内存给你哦! 
            return;
        *p = 123;
        printf("&p = %p, p = %x, *P = %d\n", &p, p, *p);
        free(p);
        printf("&p = %p, p = %x, *P = %d\n", &p, p, *p);
     //   *p = 222;
     //   printf("&p = %p, p = %x, *P = %d\n", &p, p, *p);
     //  &p = 0022ff44, p = 251010, *P = 222
     //  操作违法,报错。free之后再对其进行修改,虽打印出结果,但最后还是报错。 
       p = NULL;   //free之后一定要将p置为空,然后才能使用,否则会出现野指针。 
       printf("&p = %p, p = %x\n", &p, p);     
                     
        /*
        &p = 0022ff44, p = 251010, *P = 123
        &p = 0022ff44, p = 251010, *P = 2439224
        &p = 0022ff44, p = 0
        free之后,p的值还没有改变,只是斩断了p与之前malloc开辟的那段内存之间的关系,
        即那段内存已经不是属于p的了,系统可能已经把该块内存分配给别的程序了,p不能擅自更改。
        以上结果知,仍可以通过p访问内存,但不能修改(修改后报错),而且访问的值可能已经改变了
        (第二次打印结果*P = 2439224)。 
        
        free之后一定要将p置为空,然后才能使用,否则会出现野指针。
        指针p的类型以及它所指的内存的容量事先都是知道的,所以语句free(p)能正确地释放内存.
        如果p是 NULL指针,那么free对p无论操作多少次都不会出问题。
        如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。        
        */

通过以上内容,应该能对指针变量的本质有个清楚的认识。

补充:

指针与数组:

数组名作为指针使用的时候,它步进的大小是多少,a与&a的区别。

      
        int a[3] = {1,2,3};
        printf("&a = %d, a = %d, *a = %d, *&a = %d\n", &a, a, *a, *&a);                       
         
        int* p1 = a;
        int* p2 = &a;
        printf("p1 = %d, *p1 = %d\n",p1,*p1);
        printf("p1+1 = %d, *(p1+1) = %d\n",p1+1,*(p1+1));
        printf("p2 = %d, *p2 = %d\n",p2,*p2);
        printf("p2+1 = %d, *(p2+1) = %d\n",p2+1,*(p2+1));
        printf("a+1=%d, &a+1=%d\n", a+1,&a+1);       
               
        /*
        p1 = 2293552, *p1 = 1
        p1+1 = 2293556, *(p1+1) = 2
        p2 = 2293552, *p2 = 1
        p2+1 = 2293556, *(p2+1) = 2
        a+1=2293556, &a+1=2293564
        小结:p1和p2都是指向数组的首元素的首地址,所以其加1都是加上sizeof(int)。
              a指的是数组首元素的首地址,所以a+1 = a + sizeof(int) = a + 4;
              &a是数组的首地址,所以&a+1 = a + sizeof(int)*3 = a + 12. 
        */


数组指针:

        
        int a[3] = {1,2,3};
        int (*p3)[2] = a;  
        printf("p3 = %d, *p3 = %d, **p3 = %d\n",p3,*p3, **p3);
        printf("p3+1 = %d, *p3+1 = %d\n",p3+1,*p3+1);
        
        /*
        p3 = 2293552, *p3 = 2293552, **p3 = 1
        p3+1 = 2293560, *p3+1 = 2293556
        p3是一个数组指针。
        p3指向的是数组首地址(相当于&a),*p3指向的是数组首元素的首地址(相当于a),所以它们的值相等。
        但它们的意义是不同的, p3+1 = p3 + sizeof(int) * 2,2是数组指针指向的数组的大小。
        而*p3+1 = p3 + sizeof(int)。          
        */

指针作为函数参数

  void modify1(char *p){
       p = "world1";
  }
  
   void modify2(char **p){
       *p = "world2";
  }

  int main()  
    {  
               
        char *str1 = "hello1";
        printf("str1 = %d\n",str1);
        modify1(str1);
        printf("str1 = %s\n",str1); 
        printf("str1 = %d\n",str1);
         
        char *str2 = "hello2";
        printf("str2 = %d\n",str2);
        modify2(&str2);
        printf("str2 = %s\n",str2);
        printf("str2 = %d\n",str2);
        /*
        str1 = 4206606
        str1 = hello1
        str1 = 4206606
        
        str2 = 4206635
        str2 = world2
        str2 = 4206599
        函数参数是传值的,即使是引用作为参数,传的也是值,是引用的值,即地址。
        调用modify1时,传递的是str1指向的地址值,即形参p得到传入的值,也指向
        “hello”,然后在函数中,p的值改变,指向“world”,但此操作不会影响
        实参值str1。所以str1的指向不变。如结果所示。 
        调用modify2时,传入的是str2指针的地址,即&str2。假如“hello”的地址是
        A,str2内保存的是A,而其自身地址为B,则传入的就是B,即P内保存的是B。
         此时,*p就是B,修改操作实质上是将"world"的首地址赋值给B处保存的指针,
         即将str2与world的地址绑定,指向world。所以,输出world。
         可以看到,str2保存的地址之改变了。 
        */





 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值