zoukankan      html  css  js  c++  java
  • 伤脑筋的指针初探

    说起指针,这是C语言的强大之处,但也是伤脑筋之处,就像那句没有使用过C语言和C++导致内存泄露而产生严重问题的人永远不能理解java设计的初衷,而指针正是导致内存泄露的罪魁祸首。

    1.指针是什么?

    就像宾馆里每一个房间都有一个门牌号一样,一个房间住一个客人,我们的内存也都有一个编号,每一块内存也都有一个值,这符合我们的生活。而这个编号就是地址,这个值就是内存内容,在一个程序中,程序经过编译,变量名直接转化为地址(PS猜想:这也可能是变量名不占内存空间的原因),然后通过这个编号(地址)见到里面的人(得到相应的内存内容)。那么有没有些想法呢?我们是不是可以直接把这个编号保存起来,然后用这个编号来调用这个值。这样指针与指针变量就可以说就合情合理了,我们把一个变量的地址叫做指针。

    我们都知道 类型+*+变量名,这里其实不叫声明一个指针,这是得到一个指针变量,然后这个指针变量可以保存一个地址(就是这个变量的指针),那么这就相当于我们知道要去房间的门牌号。

    注意:*在声明指针时,仅仅表示这是一个指针变量,就像%d在输出时表示整型一样。而除了声明之外,其他时候它都表示指向对象,就是指向那一段内存空间的首地址。

    ??这里有一个疑问,如果变量名代表的是首地址的话,那么赋值的时候为什么要是  (指针类型)变量名 = &变量名 ?无奈我只有去看看编译原理及实现了。

    综上,其实指针就是一个变量的地址,而我们一般所说的指针是指针变量。

    但是,其实有些书上,比如《C语言入门经典》上就说可以储存地址的变量叫指针。这个真的很混乱,仁者见仁,智者见智吧。

    2.有点难的指针与数组。其实数组名就是首地址嘛,这个真没疑问,所以我们可以用一个指针变量将它保存起来,然后来调用数组中的值,比如如下代码:脑残的输入三个数,再输出来。

    #include<stdio.h>
    void main(){
        int a[3];
        int *p;
        int i ;
        p=a;
        for(i=0;i<3;i++){
            scanf("%d",&a[i]);
        }
        for(i=0;i<3;i++,p++){
            printf("%d",*p);
        }
    }

    然后就发现&a[i] 不可以用a++这样来代替,这是因为数组名虽然也是一个地址,但是地址也是有常量与变量之分的吧,数组名就是一个常量。

    其实数组与指针最难的就是多维数组与指针了。其实得到地址与取值并不难,难的是多种多样的表示方法下,还保证内存不泄露,保证指针变量与数组的对应关系。

    有两种感到奇葩的表达方式*p++和*(p++)竟然相同,而且竟然是先取值,再让p+1,其实不用大惊小怪,a++前有运算符都是这样的规则。

    #include<stdio.h>
    void main(){
       int a= 0;
       printf("%d",-a++);
    }

    输出是0。

    在这还有一条经验,那些大牛在用数组做参数时并不写数组,而是一个指针,这和C语言自带的函数吻合

    3,指针的两个应用,动态申请内存和字符串操作

    其实字符串操作就是数组操作嘛,动态申请内存呢,解决了一个想了好久的问题,实现长度可变的数组,有没有java中ArrayList的感觉呢?和ArrayList相比,其实他还是需要输入申请多少内存的,就是下面n的值。

    动态内存分配即分配内存大小在运行时才确定,一般在堆中分配

    对于malloc函数:

    根据不同的电脑使用状况,申请内存有可能失败,失败时返回NULL,因此,动态申请内存时,一定要判断结果是否为空。malloc()的返回值类型是“void *”,因此,不要忘记类型转换。(许多人都省略了。)

    写一个例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char *p ;
    
        p = (char *)malloc(40 * sizeof(char)) ;
        if (p == NULL) {          //这个判断是必须的
            printf("内存分配出错!");
            exit(1);
        }
        strcpy(p, "这是劝学网C语言教程。
    ");   //不要忘记给新内存赋值
        printf("%s", p);
    
        free(p);    //过河一定要拆桥
        p = NULL ;  //释放后的指针置空,这是非常好的习惯,防止野指针。
    
    return 0;
    }

    对于calloc函数:

    int *p = calloc(n,size);

    calloc可以分配n个size大小的内存空间,一般用于一组struct结构体的分配。

    那么calloc和malloc有什么区别呢?抛开n参数不谈(malloc也可以将参数设置为n*size达到一样的效果),最关键的区别是malloc分配的内存是不保证初始化的,而calloc会将分配的内存都初始化为0.

    对于realloc函数:

    realloc函数将ptr指向的内存空间重新分配大小为size并返回新的内存首地址。具体的实现,函数首先会尝试直接在已经分配的内存后进行padding,如果空间足够那么还是返回原来的地址,如果不够,则会寻找新的空间并malloc size个字节,之后再将原先的内容“搬家”到新的内存地址,所以函数的返回值可能和原指针相同,也可能不同。

    另外,size参数如果是0,则该函数和free效果相同。如果ptr是NULL,函数的效果和malloc相同~

    对于free():

    释放前三个函数申请的内存空间。关于free最经典的问题就是内存泄露(memory leak)。所以,使用前三个分配函数分配的内存一定要记得free掉

    有人对某一只在函数内使用的指针动态分配了内存,用完后不释放。其理由是:函数运行结束后,函数内的所有变量全部消亡。这是错误的。动态分配的内存是在“堆”里定义,并不随函数结束而消亡。

    有人对某动态分配了内存的指针,用完后直接设置为NULL。其理由是:已经为NULL了,这就释放了。这也是错误的。指针可以任意赋值,而内存并没有释放;相反,内存释放后,指针也并不为NULL。

     唉、、、、、学艺不精啊,编译原理和堆和栈的知识还的自己去学啊!fighting。。。。。。。。。。。。。。

    又是愉快的一天吧。。。。。。。。。。。

  • 相关阅读:
    Google Protocol Buffers学习
    C学习笔记-一些知识
    前端相关
    Spark笔记-gz压缩存储到HDFS【转】
    maven笔记-将本地jar包打包进可执行jar中
    Spark运行时错误与解决
    机器学习笔记
    Spark笔记-DataSet,DataFrame
    云平台各层解释
    linux笔记-多服务器同时执行相同命令
  • 原文地址:https://www.cnblogs.com/969059506-java/p/3675830.html
Copyright © 2011-2022 走看看