V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
whoami9894
V2EX  ›  C

请教一个 C 语言内存分配的问题

  •  
  •   whoami9894 · 2018-05-27 12:39:47 +08:00 · 3371 次点击
    这是一个创建于 2406 天前的主题,其中的信息可能已经有所发展或是发生改变。

    C 语言的malloc()函数分配的是应该算是动态内存还是自动内存?我记得应该是动态内存,只要我没有 free,它就一直存在。但我在 void 函数中初始化一块内存,函数调用结束后这个指针也变成未定义了,难道这里分配的内存作用域只在函数内部吗

    owenliang
        1
    owenliang  
       2018-05-27 12:41:49 +08:00 via Android
    好的
    zwy100e72
        2
    zwy100e72  
       2018-05-27 12:44:54 +08:00   ❤️ 1
    你在函数内部调用 malloc 分配内存,但是没有把内存指针保留下来的话,函数退出后这部分内存没有释放,你也无法访问,那么就是内存泄漏了。
    任何程序都应当避免内存泄漏。
    adadada
        3
    adadada  
       2018-05-27 12:45:15 +08:00   ❤️ 2
    第一个问题:动态内存。第二个问题:函数里声明的指针*变量*的作用域在函数体里,但是它指向的内存的不是,你可以把内存的地址返回出来,在函数之外继续用。
    v2exchen
        4
    v2exchen  
       2018-05-27 12:48:16 +08:00 via Android
    你是通过什么方式把内存地址返回的,是 return。还是传参的方式,如果是传参的方式需要用指针的指针
    whoami9894
        5
    whoami9894  
    OP
       2018-05-27 12:49:54 +08:00
    @zwy100e72
    感谢回复
    那应当函数返回分配的指针,然后 free 掉函数中的那个指针吗。但是函数中 return 和 free 的顺序该怎么写
    whoami9894
        6
    whoami9894  
    OP
       2018-05-27 12:51:54 +08:00
    @adadada
    嗯那就是应该函数设返回值,返回那个指针指向的地址。那函数声明的指针需要显式的 free 吗
    whoami9894
        7
    whoami9894  
    OP
       2018-05-27 12:53:37 +08:00
    @v2exchen
    我开始的错误理解是函数中分配的指针应该一直存在,所以函数没有返回值。现在懂了,应该返回分配的地址,函数中的指针不是一直存在
    verrickt
        8
    verrickt  
       2018-05-27 12:55:02 +08:00 via Android
    malloc 分配的内存是在堆上的(进程地址空间的 data 区),需要显示 free

    > 但我在 void 函数中初始化一块内存,函数调用结束后这个指针也变成未定义了

    没理解你的意思,如果是这样
    ``` c
    int* ub()
    {
    int i;
    return &i
    }
    ```
    的话,i 实际是分配在栈上的局部变量,函数返回后栈帧弹出,试图访问&i 的已经是未定义行为(ub)了。

    总结来说,搞明白地址在栈上还是堆上就可以判断栈帧回弹之后使用指针是不是 ub 了
    whoami9894
        9
    whoami9894  
    OP
       2018-05-27 12:57:24 +08:00
    @verrickt
    ```c
    void get_cc(){
    int* poi = (int*) malloc(sizeof(int*));
    int ll = 1;
    poi = ≪
    }

    void main(){
    get_cc();
    printf("%d",*poi);
    }
    ```

    我的意思是这样的,我错误理解 poi 指针会一直保存,但应当是那块地址会保存,poi 随函数销毁
    adadada
        10
    adadada  
       2018-05-27 12:58:28 +08:00
    @whoami9894 #6 #6 如果没有其它的办法在函数之外释放,那就需要在函数体里 free。
    whoami9894
        11
    whoami9894  
    OP
       2018-05-27 13:04:24 +08:00
    @adadada

    好的,明白了,感谢
    momocraft
        12
    momocraft  
       2018-05-27 13:16:49 +08:00
    分不分配内存都 "存在", 只是 malloc()之后 free()之前这段时间归你用
    whoami9894
        13
    whoami9894  
    OP
       2018-05-27 13:29:08 +08:00
    @verrickt

    您好还想请教一下:像您说的那种情况,应当如何避免返回局部变量的地址呢?是直接传变量的副本作返回值吗?
    WordTian
        14
    WordTian  
       2018-05-27 13:34:41 +08:00 via Android
    @whoami9894 #9 总结一下,大致就是这样了

    int* poi = (int*) malloc(sizeof(int*));

    左边的变量在栈区,get_cc 函数结束后,该函数的栈弹出,变量销毁

    malloc 分配的内存在堆区,然后把分配的内存的地址传给栈区的 poi 指针

    main 中调用 get_cc 结束后,poi 变量消失,但堆区中分配的内存还在,这时就造成了内存泄漏
    whoami9894
        15
    whoami9894  
    OP
       2018-05-27 13:55:25 +08:00
    @WordTian
    对的,感谢
    verrickt
        16
    verrickt  
       2018-05-27 14:51:39 +08:00   ❤️ 2
    @whoami9894 还是没看懂你的意思。
    针对这个情况画了张[简略的图]( https://www.draw.io/?lightbox=1&target=blank&highlight=0000ff&edit=_blank&layers=1&nav=1&title=Untitled%20Diagram.xml#R7Vtdb%2FMmFP41SO2kVrbx52XcpNvFXmlSJ227pAlJ3Doms0mTvr9%2BYOMvIInbOKnbNRctPmA%2BnvMcOBwwgHer3a8pWi9%2FkBmOgWXMdgCOgWWZhm2xf1zyWkgczykEizSaiUK14CH6ics3hXQTzXDWKkgJiWm0bgunJEnwlLZkKE3Jtl1sTuJ2q2u0wIrgYYpiVfpXNKPLQupbXi3%2FDUeLZdmy6QZFziOaPi9SsklEe8CC8%2FxXZK9QWZcYaLZEM7JtiOAEwLuUEFqkVrs7HHNsS9iK9%2B735Fb9TnFCu7wAixdeULwRQ%2F8T76joG30t8chHhPk7BoDhdhlR%2FLBGU567ZQxgsiVdxezJZElRJU4p3u3tllkNlpEIkxWm6SsrUr7gCHwEf2wonre1NsySLMuGJgIhQ4IAi6rqGgSWEDjoMfEUTMaIosFhYnXFxDJ6AMVXQHmgjOuDQ8XpCIrp9wBKoICy890byGRuzNoJH1OWWvAUmPggCEAwAhMHBHcgNPLEGLBuTDwwCkDIslgBD4S2AirlNtlCLqMpecZ3JCYpkyQkYSXDeRTHkgjF0SJhj1MGJ2bykIMdsYluJDJW0WzGm9Gqqq3MHrRlSRR2VW35GmXZzum6KknQUJaCM05mI756cMBilGXRVMdXPFMWj6PjbwzQ0QywlKU4RjR6aVevG7Vo4Q8SsYZrY5DwdSyJ5BnZpFMs3mouC0cqcqV6KEoXmCr15DqoRt1NLaaiFmNniN%2FXNQRbUpSpGoKtncp7MARLg%2Fi9%2BH1dxI%2BvE2cDXHWyvtzMU03bErxvnXjkeion5wwzj6OqZeKCkQF8ny%2FOoQ9GHk%2BMLBBM8sWZCe95mcAFYZAv4AYIzA42k%2BIs%2Boke8wKcz2ve%2F3xETgic8ZusJUaPOA6rfUZpdfVO44C5iP2S6Amo6N3iETxoRzfGLdNS0NKSeRqHyiJkPs%2FwyWp1j1vbpZ1Ux%2FFunWPTT7XmttxUr48JSN3RDBCSiozHMenDHVR999Jrn0UvLWzcfzd8Ox5ys74RdjpiJWI8p3Vu5emLWrI1SkpZQfMVipKr6zKfdbBZpCHO229L%2B%2B8S8MKP6QlacTIlj9k6fzY0ogKuiGeWy%2F8AuvQL%2B%2FdU9KlZpvgbDamXz1Uvr7jkeoXimEyv2BKEyZyLrq8H0V2KM3p1ga5oyD8eiBm%2BEDYhVUgMo08fNjWoPelEI1lUTB%2BbygikGeSTDqg13fCFqxrf5kuMz9iWA5LLHxxZU9ZxtusOkK7kcGYjpfBX3bp7nrS5rFzHhquoDRyysqe7iprYdzRA%2F7nrlkLemr8HEqiJYMwW%2BEE8kpQuyYIkKJ7UUokXDTjwLqJ%2FCzFP%2F8PTbHTsKWEdq7L4Q533hCl9FUeHaEMJE9Xt%2Fk7I%2Bo2hEzaaPEghlC72B0W8oUkEVSWd972dGaduTp4GyDjT6Eg5OXz8Hkycs1DO3E8589KUg%2BVJd4Ny0D2RcvmrozRFr40CIvy0N5rnScFSM5BOkY%2BUd8UOvVZt0YP3xnSgekjw8SePGnvQHGedzR5gh6jyxSGxZUhczcGGFpLqfO4kTNSQbuEN8lit7wPf5WFcn6XH%2FNQ1GAHfzrNcLuSh3jEI%2FM8MYx8npVAXQVXd1nch281zHQDgjuph6gHvg7aqg7n5eEzUAK3pX256s%2BE5lvsheZhQ42HCS3mYUPUwX4ZIOe%2BSlHPPQbkheZjlZNWinHMhypWNNyi3HSDlILwk5dSZX4EkW6I1T85jvBN3BELtdYGMqZUevEXQDPWo58XDvV1gB4qWAsnR6XrBQK2qWtb6v2Jg7z9kbPo%2FPPrf8JqUOwjvjvd9pmsH9p6lT9Rk3FpOeWYrFHcDTyNXr9cOHHV2%2B7bkbubXmyU7Uk39GbKjBiB%2BoCg5aLRfyjodQ8%2Ba%2BlKQCa22Nm7cIZlnh2jJt3kyF7gdyPPl85POxilV5MrhnR5tU3OPT7PIFt9w%2FB9WUufwBT5uqrClHHtIhtrh%2Bt63oVq2dIHclr%2Fx6Wyo8k3089mpegtR%2FwnRJ7e%2BPSc2lfUZwQcujOyx%2FriwKF5%2FwQkn%2FwE%3D)

    代码如下:

    ``` c
    int main()
    {
    int i =0;
    int* j = &i;
    int* k = (int*)malloc(sizeof(int));
    test();
    }
    void test()
    {
    int u = 0;
    int& v = u;
    int w = (int*)malloc(sizeof(int));

    }
    ```

    当前执行的是`int w = (int*)malloc(sizeof(int));`。
    Data 和 Stack 的内存是完全分开的。函数里定义的变量都是局部变量,分配在当前栈帧上。当函数返回后,栈帧回弹,局部变量的存储空间就被回收。当`test`执行完成,返回到`main`时,u,v,w 的存储空间就被回收了。

    但是 Data 区的内存并不会因为函数返回就被回收,而是需要你自己去调用 free。


    > 您好还想请教一下:像您说的那种情况,应当如何避免返回局部变量的地址呢?是直接传变量的副本作返回值吗?
    直接返回*值*是一种办法,但 C 中传参的拷贝语义,有时为了避免拷贝比较大的结构,也可以让调用者传指针进来,这样可以避免拷贝。还有一种方法,在被调函数中使用 malloc 分配内存。这三种方法孰优孰劣要看具体的情况。我只用过 C 写玩具,也就没法给你建议了。

    第二种方法大概是这样的
    ``` c
    typedef struct
    {
    int[500] a
    } very_big_structure;
    int main()
    {
    very_big_structure t;
    callee(&t);
    }
    void callee(very_big_structure* ptr)
    {
    //使用 ptr
    ptr->a[0]=4;
    }

    ```
    verrickt
        17
    verrickt  
       2018-05-27 14:54:18 +08:00
    @whoami9894 如果还是不太懂的话,可以看一看 CSAPP 的 Machine-Level Representation of Programs 部分
    whoami9894
        18
    whoami9894  
    OP
       2018-05-27 16:10:21 +08:00 via Android
    @verrickt
    感谢感谢!您回复的太认真了
    zhicheng
        19
    zhicheng  
       2018-05-27 16:23:04 +08:00
    @whoami9894 你 9 楼的代码编译都过不去的,不懂的写个代码实验一下,不要读死书。你不理解的地方是你没有区分开变量和值。
    rosu
        20
    rosu  
       2018-05-27 16:37:40 +08:00 via Android
    @whoami9894 从你 9 楼的代码中,我觉得你可能还需要去了解什么叫生命周期。

    感觉你 C 基础不是很稳固。建议选对教材,然后多实践。强烈推荐 C primer plus。
    ipwx
        21
    ipwx  
       2018-05-27 16:44:09 +08:00
    操作系统不会无缘无故把一块物理内存给某个进程,有需要的话,要申请。

    但是每次向操作系统都申请太慢了,所以各编程语言其实是一大块一大块地向操作系统申请的。

    每次有个小内存的 malloc,编程语言先试图从已经申请但没有用的内存里面切一块给用户程序。如果不够,再向操作系统申请。

    而所有已经向操作系统申请的大块内存、以及切分方案,各个编程语言在全局变量空间自行维护。

    函数里面的临时内存空间那是栈空间,不归 malloc 和编程语言的这套机制管。
    whoami9894
        22
    whoami9894  
    OP
       2018-05-27 16:48:16 +08:00 via Android
    @zhicheng
    ……那段代码是告诉他我当时理解的错误。贴子主体就说了编译器会报未定义。
    whoami9894
        23
    whoami9894  
    OP
       2018-05-27 16:51:13 +08:00 via Android
    @rosu
    那段代码局部 int 和指针变量会随函数销毁,我的理解错误是没分清指针和内存,就像楼上说的没区分指针变量和内存的值
    zhicheng
        24
    zhicheng  
       2018-05-27 18:30:40 +08:00
    @whoami9894 不要强行去解释。

    未定义错误和销不销毁没有关系,未定义错误是编译时期的事。你本来就理解错了,就不要用另一种错的方式去理解。建议还是看看书,一个字一个字的看,不要跳着看。
    afpro
        25
    afpro  
       2018-05-27 18:31:45 +08:00
    这和内存没什么关系吧 明显是楼主还没分清内存和指针的关系
    whoami9894
        26
    whoami9894  
    OP
       2018-05-27 18:54:10 +08:00 via Android
    @zhicheng
    我用哪种错误方式去理解了?
    我最开始是想着定义 void 函数分配一块内存,认为内存会一直存在,这里没分清楚指针和内存,错误认为函数内定义的指针等同于函数内定义的内存一样会一直存在直到 free。说到底不是变量作用域存活周期那些问题,是指针和内存问题
    whoami9894
        27
    whoami9894  
    OP
       2018-05-27 18:59:45 +08:00 via Android
    @afpro
    你在说哪儿
    zhicheng
        28
    zhicheng  
       2018-05-27 19:00:17 +08:00 via iPhone
    @whoami9894 我说了 9 楼的代码是错的呀,如果你觉得那个代码没错,那 You are right.
    whoami9894
        29
    whoami9894  
    OP
       2018-05-27 19:11:00 +08:00 via Android
    @zhicheng
    我发这个贴子就是因为理解错误所以写出那样的代码,之后你又说我在强行解释,"用另一种错误方法理解"。接着我问你是哪种错误方式,你又兜回 9l,我从没说 9l 是对的吧大哥,就是因为它错了我才来发帖问的。
    zhicheng
        30
    zhicheng  
       2018-05-27 19:12:00 +08:00 via iPhone
    @whoami9894 你是对的。
    liuhaotian
        31
    liuhaotian  
       2018-05-27 19:16:49 +08:00
    @zhicheng #24
    @afpro #25
    @whoami9894 #22

    只有我一个人觉得 9#的代码最大的问题是变量作用域的问题吗

    编译器里报的未定义 是 not declared variable,准确来说是 变量未声明
    而上面几位说的未定义 ub 是 undefined behavior
    似乎楼主认为这两个未定义是同一个东西了
    zhicheng
        32
    zhicheng  
       2018-05-27 19:20:37 +08:00 via iPhone
    @liuhaotian 其实我想了一下,觉得他大概看书看到赋值那一章就能搞懂了。
    whoami9894
        33
    whoami9894  
    OP
       2018-05-27 19:22:53 +08:00
    @liuhaotian
    对啊,指针变量的作用域只在函数内部。我没分清指针变量和内存,以为**指针**这个变量像**动态内存**一样。就是因为理解有问题.....有点说不清
    OneNian
        34
    OneNian  
       2018-05-27 19:26:57 +08:00
    先把 C 语言基础看完。(加油吧这基础差的有点多)
    OneNian
        35
    OneNian  
       2018-05-27 19:28:41 +08:00
    main 里声明一个指针变量,函数 return 地址。

    方法有很多。不急。多看书做题
    pkookp8
        36
    pkookp8  
       2018-05-27 20:45:53 +08:00 via Android
    白看了好久。。。9 个字,自己搜吧
    局部变量和全局变量
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3430 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 04:52 · PVG 12:52 · LAX 20:52 · JFK 23:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.