就这么 Stack overflow 了,一定是有什么我不知道的地方。求赐教。 - V2EX
raincious
V2EX    C

就这么 Stack overflow 了,一定是有什么我不知道的地方。求赐教。

  •  
  •   raincious Feb 27, 2015 2560 views
    This topic created in 4095 days ago, the information mentioned may be changed or developed.
    直接上代码:


    猜想是:
    当我通过ptest访问SubItems时,map会自动帮我初始化好对应的ContainerStructEnitiy实例,这样我就能继续拿新的ContainerStructEnitiy的地址继续往里爬,直到一个树杈完全建好。

    现实:Stack overflow

    VS告诉我出错的代码是在xmemory0或xtree里(多个地方),具体是这段:


    看起来像是释放的时候出现的问题。

    我测试了下,将loop的数值改小(比如改到10、128、256)就不会出现这种情况。最后发现的阀值是338,如果loop超过338就会遇到Stack overflow,小于则不会。不知道会不会是因为我遇到了某种限制。

    为什么会出现这样的错误呢?请赐教。

    感谢。
    20 replies    2015-02-28 13:08:32 +08:00
    gamingcat1234
        1
    gamingcat1234  
       Feb 27, 2015
    不是说了是stack overflow吗?
    http://en.wikipedia.org/wiki/Stack_overflow
    你这个奇妙的嵌套数据结构里所有东西都分配在stack上。
    raincious
        2
    raincious  
    OP
       Feb 27, 2015
    @gamingcat1234

    感谢,貌似这个数据结构确实不太好,但是不知道怎么实现类似的,于是就糟弄了一把。

    但,我想知道的是如何防止这个Stack overflow的问题。关键是在我不知道为什么会有那个Magic number 338在。
    gamingcat1234
        3
    gamingcat1234  
       Feb 27, 2015   1
    map <wstring, ContainerStructEnitiy*> SubItems;
    然后用new和delete自己分配内存
    sumhat
        4
    sumhat  
       Feb 27, 2015   1
    取决于你的 stack 大小和结构体的大小,你可以在 VS 里调大 stack size,然后数值就不是 338 了。
    raincious
        5
    raincious  
    OP
       Feb 27, 2015
    @gamingcat1234
    其实我不想手动管理那些内存哈。不过分配在堆上我没试过,或许能解决这个问题?

    @sumhat
    目前看来这就是解决方案了。但貌似真正的问题恐怕是这个数据结构有问题哈。
    zcbenz
        6
    zcbenz  
       Feb 27, 2015
    @raincious 你的头像好烦……
    yangff
        7
    yangff  
       Feb 27, 2015
    linux gcc 4.9.2 20150204 复现不能.
    raincious
        8
    raincious  
    OP
       Feb 27, 2015
    @zcbenz
    呵呵,其实……这样才有特点……

    @yangff
    或许是GCC的栈尺寸比较大?我在VS2013Express下面写的。
    yangff
        9
    yangff  
       Feb 27, 2015   1
    是这样的,你分配在哪里都没有用.
    你前面的代码都没有问题, 思路也没有问题.
    但是结束的时候, stl会尝试析构ContainerStructEnitiy.
    这肯定是要的..那么问题来了, stl会递归的析构ContainerStructEnitiy下面的各种对象, 比如那个map, map析构的时候又会递归的析构更下面的ContainerStructEnitiy,如此往复, 最后这个析构就把栈炸了.
    yangff
        10
    yangff  
       Feb 27, 2015   1
    唯一的办法就是...用指针. 然后手动维护对象生命周期.
    不难发现,就算用智能指针也都会有析构的时候爆栈的问题...所以只能手动维护了..
    raincious
        11
    raincious  
    OP
       Feb 27, 2015
    @yangff

    嗯,这解释了为什么会在释放的时候出问题。

    事实上之前我也尝试过用ContainerStructEnitiy*以及new和delete构建相同的功能(只是ContainerStructEnitiy变成了Class,有自己的析构来释放自己SubItems里的对象),然后发现只要我不叫delete,就不会出现问题(虽然这样真的有问题)。

    太悲剧了,问有办法解决这个问题么?还是我只能放弃这个结构了?
    yangff
        12
    yangff  
       Feb 27, 2015   1
    @raincious
    ContainerStructEnitiy 改成ContainerStructEnitiy*, 然后把所有的ContainerStructEnitiy*放在pool里面存起来手动delete 一.个.一.个.的delete.
    Cee
        13
    Cee  
       Feb 27, 2015   1
    yangff 的,用指即可。

    有不是「阀(fa)值」,是「阈(yu)值」。
    raincious
        14
    raincious  
    OP
       Feb 27, 2015
    @yangff
    @Cee

    好的好的,我就这么办。

    竟然有两个大神来到了我的帖子里,真是受宠若惊。
    slowman
        15
    slowman  
       Feb 27, 2015   1
    #include <iostream>

    #include <map>

    using namespace std;

    struct ContainerStructEnitiy
    {
    wstring Name;
    map <wstring, ContainerStructEnitiy> * pSubItems;
    };

    int main()
    {
    int loop = 102000;

    ContainerStructEnitiy test;
    ContainerStructEnitiy *ptest = &test;

    while (loop-- >= 0)
    {
    ptest->pSubItems = new map <wstring, ContainerStructEnitiy>;
    (*(ptest->pSubItems))[to_wstring(loop)].Name = L"blablabla";
    ptest = &(*(ptest->pSubItems))[to_wstring(loop)];
    cout << loop;
    }

    return 0;
    }

    这样行 C++ 好久没用了,凑活看
    刚才还没上面的回复。。看来人还是很多的
    raincious
        16
    raincious  
    OP
       Feb 27, 2015
    @1423

    感谢。但是不太理解呢。

    是说在时候手动清理内存么?因为我看到了new map,测试之后发现函数运行完成返回后内存并没有释放,估计肯定还在堆里。

    然后为了偷懒我把代码改成了这样,结果Stack overflow(嗯,就是又造成了我之前的问题):
    https://gist.github.com/raincious/0f8b527de148e503bd03
    可能我没理解对。

    难道要完全手动管理么?就是new出map的那些地址也要单独保存下什么的然后在最后一起释放?
    yangff
        17
    yangff  
       Feb 27, 2015   1
    @raincious
    就是说
    class ContainerStructEnitiy
    {
    public:
    wstring Name;
    map <wstring, ContainerStructEnitiy[*]<<<<---这里要用指针> (*)<<<<<<---这里不用指针没问题pSubItems;

    ContainerStructEnitiy();
    ~ContainerStructEnitiy();
    };

    这里的问题在于, 析构ContainerStructEnitiy的时候会析构里面的map, 而map会析构他包含的所有元素,也就是那一堆ContainerStructEnitiy, 这个过程是递归进行的, 因此会造成栈溢出. 不管你是用delete还是c++自己析构, 都会导致这个问题.

    解决的办法在于把map对ContainerStructEnitiy的直接管理改成对ContainerStructEnitiy*也就是指针的管理,这样c++在析构map,只会释放保存ContainerStructEnitiy*的内存, 而不会进一步析构他所包含的ContainerStructEnitiy, 然后, 你手动记下所有的ContainerStructEnitiy*, 然后自己把他们delete掉, 就没有问题了.
    gamingcat1234
        18
    gamingcat1234  
       Feb 27, 2015   1
    你这个问题的关键是使用了递归型的tree操作,tree的深度到了一定程度,怎么都会有call stack溢出的问题。即使你用一个pool来手动管理,要遍历或删除tree的一个分支时,只要还是递归型的tree操作,那还是老问题。解决方法是把tree操作写成非递归型的,请google non-recursive tree operation。
    当然,对你的特定问题,如果不进行什么分支操作,能接受最后整颗树一起释放,那你把每次new出来的指针保存在一个list里最后统一delete就行了,即yangff的方法。
    slowman
        19
    slowman  
       Feb 28, 2015 via Android   1
    。。。这不是析构的问题。。而是你那个 test 对象是在栈上的,他内部又有一个 map 而不是 map 的指针,你在循环中不断增大这个 map 的大小,他越来越大,就爆了,不过在 Mac 上想爆还真是得等好一会。
    最后想说一个类的大小最好是常量,你这里完全可以用指针或引用
    raincious
        20
    raincious  
    OP
       Feb 28, 2015
    @yangff
    @gamingcat1234
    @1423

    好的了解。感谢指导。
    About     Help     Advertise     Blog     API     FAQ     Solana     3027 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 42ms UTC 08:46 PVG 16:46 LAX 01:46 JFK 04:46
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86