vector<T> 和 T[] 内存地址区别 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aigebiu
V2EX    C

vector<T> 和 T[] 内存地址区别

  •  
  •   aigebiu 2016-05-05 16:19:14 +08:00 3842 次点击
    这是一个创建于 3451 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 libnetfilter_queue 获得数据包, libtins 操作数据包,放回 netfilter 时出现问题:

    vector<uint8_t> vec = pkt.serialize(); nfq_set_verdict(qh, id, NF_ACCEPT, size, &vec[0]); 

    上面这样,(据我测试)只有长度 15 字节以上的数据包才能成功放回,而这样:

    uint8_t arr[MAX_BUFSIZ]; copy(vec.begin(), vec.end(), arr); nfq_set_verdict(qh, id, NF_ACCEPT, size, arr); 

    就完全没问题。

    为什么呢?&vec[0] 和 arr 的地址有什么区别么?

    按理说 vector 也是一段连续内存,应该一样的啊。求解,多谢!

    第 1 条附言    2016-05-05 18:28:25 +08:00
    size = vec.size();

    摘一些代码实现,也许有用

    ```c++
    PDU::serialization_type PDU::serialize() {
    vector<uint8_t> buffer(size());
    serialize(&buffer[0], static_cast<uint32_t>(buffer.size()), 0);
    return buffer;
    }
    ```

    ```c++
    static int __set_verdict(...const unsigned char *data...)
    {
    ...
    /* The typecast here is to cast away data's const-ness: */
    nfnl_build_nfa_iovec(&iov[1], &data_attr, NFQA_PAYLOAD,
    data_len, (unsigned char *) data);
    ...
    return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
    }
    ```

    基本就是调 nfnetlink ,先包装成 iovec (话说外面把 vector 拆成 size 和*data ,里面又组合起来累不累啊……直接传 iovec 多好 233 ),然后 nfnl_sendiov 发给内核。不过会不会和 const 有关呢?
    第 2 条附言    2016-05-05 19:03:21 +08:00
    去掉 const 重新编译了下 nfqueue 库,一样,还是 135 字节以上就行。应该和 nfqueue 库无关,是 vector 的问题。
    kernel 4.4.0 ubuntu 14.04.01 x64
    第 3 条附言    2016-05-05 23:50:30 +08:00
    编译到树莓派 2 ( openwrt 15.05.1 )上试了下,有同样问题,不过成功/失败的临界大小变成 69 字节, 32 位 ub14 则是 61 字节;而 vector 内存 copy 一遍后,都能正常运行。

    上述场景中,变量主要是标准库和架构,其他库都是从新编译的,看来 STL 小块内存的解释最靠谱。

    openwrt 的 libc/c++: libuClibc-0.9.33.2.so libstdc++.so.6.0.19
    第 4 条附言    2016-05-06 12:57:13 +08:00
    template <typename T> class alloc { public: typedef T value_type; typedef T* pointer; typedef std::size_t size_type; alloc() throw() {} ~alloc() throw() {} value_type pool[1<<20]; size_type cur = 0; pointer allocate(size_type num, const void* = 0) { pointer p = &pool[cur]; cur += num; return p; } void deallocate(pointer p, size_type num) {} }; vector< uint8_t, alloc<uint8_t> > vec(size); 

    用这个allocator开的vector,没有上述问题。用malloc或者默认的new开的vector则存在问题。

    第 5 条附言    2016-05-06 12:59:49 +08:00
    但数组不管是静态开的,还是 malloc/new 的,都没问题。
    第 6 条附言    2016-05-06 13:20:27 +08:00

    我有点明白了…… 如下,删掉free/delete,用malloc/new就也没问题了

     pointer allocate(size_type num, const void* = 0) { return (pointer)malloc(num * sizeof(value_type)); } void deallocate(pointer p, size_type num) {} 

    不让vector回收掉内存即可。

    但回收内存时函数已返回,netlink应当已经送出iovec消息,将vector的这段内存拷贝进内核了,外面回收内存不会影响它了啊……

    第 7 条附言    2016-05-06 13:44:12 +08:00

    额……大家散了吧……

    原因是vector在scope结束时deallocate内存,然后我的vector定义在一个if里,还没等调用netlink就回收了。 去掉if或定义在外面就好了。

    不过还是有一些收获。比如知道了malloc/new会重复利用小块内存:

    alloc 29 bytes at 28261216 alloc 29 bytes at 28261216 #复用小块内存,netlink re-inject失败 alloc 233 bytes at 28261456 # re-inject成功 alloc 29 bytes at 28261216 alloc 233 bytes at 282614328 # 新开内存,re-inject成功 

    所以小包未能成功放回(回收后的内存很快挪作他用了),大包则没问题(每次都是新开的内存)。

    @MCVector 说的对,和vector没关系。(*/ω\*) 掩面而逃233

    第 8 条附言    2016-05-06 14:02:40 +08:00
    更正:不一定每次新开大块内存,取决于具体情况。

    glibc malloc 介绍 https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
    38 条回复    2016-05-07 09:46:41 +08:00
    mug
        1
    mug  
       2016-05-05 17:23:15 +08:00
    vector 的内部不一定是连续的。
    Monad
        2
    Monad  
       2016-05-05 17:38:28 +08:00   1
    反对楼上 摘自 N3690.pdf
    没用过 libnetfilter_queue, 楼主你的问题我猜测是 size 有问题...
    araraloren
        3
    araraloren  
       2016-05-05 17:43:56 +08:00
    不要这么用 vector 以及其它模板, C 接口就老老实实使用 C 数据结构。。
    kkhaike
        4
    kkhaike  
       2016-05-05 17:58:22 +08:00
    我也觉得是 size 有问题,你把他替换成 vec.size()试试
    aigebiu
        5
    aigebiu  
    OP
       2016-05-05 18:18:47 +08:00
    @Monad @kkhaike size=vec.size(); 抱歉忘说了

    我之所以问内存区别,是推测问题可能跟 vector 分配内存的方式有关(倍增 capacity )
    但 new 一个数组也没问题,和静态开的一样 就想不明白了……
    bossfrog
        6
    bossfrog  
       2016-05-05 18:49:22 +08:00 via Android   1
    @mug 标准保证连续
    boydfd
        7
    boydfd  
       2016-05-05 19:05:00 +08:00
    stl 里面小块内存(比如 128 以下)是通过内存池分配的。大于 128 才会通过全局的 malloc 分配。问题应该就出在这,你结合自己的程序考虑一下看
    aigebiu
        8
    aigebiu  
    OP
       2016-05-05 19:53:24 +08:00
    @boydfd 谢谢!应该是这个原因 vec.reserve() 一下就好了…… 能否具体讲下小块内存的“内存池”?或者相关资料?多谢
    Monad
        9
    Monad  
       2016-05-05 20:04:41 +08:00
    @aigebiu 这种东西对上层来说应该是透明的,不应该是这个影响,除非你依赖了某些分割(比如\0 )之类的,
    另外把 const unsigned char* 转成 unsigned char* ,如果传入的参数本身就是 const ,那这是未定义行为,也有可能会影响结果。
    如果可以上段完整的 gist ,我编译调一调。
    boydfd
        10
    boydfd  
       2016-05-05 20:13:08 +08:00
    @aigebiu stl 源码剖析这本书上有介绍
    allenx
        11
    allenx  
       2016-05-05 20:26:54 +08:00
    vector 内部连续,可以当做起始地址为:&vec[0],长度为 sizeof(T)*vec.size()的一段内存。
    aigebiu
        12
    aigebiu  
    OP
       2016-05-05 21:06:34 +08:00
    @Monad 截出一个片段 https://gist.github.com/isofew/3aa6c0eb716655fa60e53e46ae298d56 我觉得还是 stl 实现的问题
    neoblackcap
        13
    neoblackcap  
       2016-05-05 21:08:30 +08:00
    c++03 开始已经要求 vector 对内部存储要连续了。
    neoblackcap
        14
    neoblackcap  
       2016-05-05 21:08:46 +08:00
    @mug c++03 开始已经要求 vector 对内部存储要连续了。
    kkhaike
        15
    kkhaike  
       2016-05-05 21:27:22 +08:00
    看了下源码,我觉得原因应该是这个传入的长度一定要是 4 的倍数
    ```
    #define NFA_ALIGNTO 4
    #define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1))
    ```
    再加上 @boydfd 所说的 ,小内存是内存池,越界访问出了问题?“成功放回”指的是什么?
    反正 vector 一定是内部连续的,这样使用确实没问题。
    MCVector
        16
    MCVector  
       2016-05-05 21:28:00 +08:00 via Android
    std::vector 表示这个锅我不背
    aigebiu
        17
    aigebiu  
    OP
       2016-05-05 22:03:01 +08:00
    @kkhaike 成功将修改后的包放回 netfilter 然后走 routing->forward->postrouting->网卡 出去
    现在只是 verdict 传回去了,包的内容没传过去,还是原来的包,走 routing->input->用户进程 这条路了。
    4 字节的包一样不行。
    ryd994
        18
    ryd994  
       2016-05-05 22:28:38 +08:00
    uint8_t * arr =malloc(size*8);
    copy(vec.begin(), vec.end(), arr);
    nfq_set_verdict(qh, id, NF_ACCEPT, size, arr);

    这样会怎样?
    aigebiu
        19
    aigebiu  
    OP
       2016-05-05 22:46:25 +08:00
    @ryd994 成功放回
    colatin
        20
    colatin  
       2016-05-06 00:09:10 +08:00
    依稀记得 1byte vector 有优化,内部并不是 uint8_t []方式分配内存的
    colatin
        21
    colatin  
       2016-05-06 00:09:44 +08:00
    @colatin 记错了,是 bool 类型的
    colatin
        22
    colatin  
       2016-05-06 00:15:45 +08:00
    应该是内存池和对齐的问题
    proudzhu
        23
    proudzhu  
       2016-05-06 09:03:51 +08:00 via Android
    人家 vector 就不是给你这么用的,出问题了去看标准库实现,猜来猜去没啥用
    zwzmzd
        24
    zwzmzd  
       2016-05-06 09:21:38 +08:00
    试一下如下的代码呢?


    vector<uint8_t> v_arr(MAX_BUFSIZ);
    copy(vec.begin() vec.end(), v_arr.begin());
    nfq_set_verdict(qh, id, NF_ACCEPT, size, &v_arr[0]);


    我看了一下你提到的两种实现,第一种使用 vector 的实现,主要数据是放在堆中的;而第二种开辟临时数组的方式,数据是存放在栈上的。相对来讲,数据放在堆上很有可能因为编程者的失误导致部分区域被覆盖,建议检查下之前有没有内存越界的错误。

    C++里面我一直是使用 vector+resize()代替 malloc()的,这样有个好处是块临时变量会在块结束时自动释放。用到现在还没发现&vec[0]和 arr[]有任何区别
    linux40
        25
    linux40  
       2016-05-06 10:34:08 +08:00 via Android
    @zwzmzd &vec[0]和 arr[]没区别是建立在你用的 Alloc 是 std::allocator ,标准没有说 Alloc::pointer 一定是指针(T*)哟,标准也没有说&t 的返回值一定是指针哟。
    zwzmzd
        26
    zwzmzd  
       2016-05-06 11:01:50 +08:00
    @linux40 是指 operator& 重载么? LZ 遇到的应该不是这个问题吧
    aigebiu
        27
    aigebiu  
    OP
       2016-05-06 11:22:36 +08:00
    @zwzmzd 这样没问题。但就像我 14 楼提到的,直接在 vec.reserve(MAX_BUFSIZ)也可以修复问题,静态开一个 vector 相当于调 reserve ,仍然是在堆上分配内存吧。而且 new char[] 在堆上也没问题,应该不是堆/栈的原因。

    顺便纠正之前的几个错误,一个是 64 位 ub 下是 121 字节临界(之前忘减 14 字节 eth 头了),不过反正这个数的绝对值意义不大,也无所谓。
    另一个可能的错误是,之前认为是内存分配的原因,但我自己写了个 allocator ,每次都在全局上 new 内存,问题仍然出现,而且临界大小不变。

    我写的 allocator patch : https://gist.github.com/isofew/2ffa93faf6ebe3899de55b87115e1551

    @colatin
    @kkhaike
    @boydfd
    @linux40

    uint8_t arr[MAX_BUFSIZ] 静态开输出如下:
    alloc 29 byte(s) at 30174048
    &vec[0]: 30174048, arr: 140734511709248
    dealloc 29 byte(s) at 30174048

    alloc 128 byte(s) at 30174208
    &vec[0]: 30174208, arr: 140734511709248
    dealloc 128 byte(s) at 30174208

    new uint8_t[size] 动态开输出如下:
    alloc 29 byte(s) at 21465952
    &vec[0]: 21465952, arr: 21466000
    dealloc 29 byte(s) at 21465952

    alloc 128 byte(s) at 21466160
    &vec[0]: 21466160, arr: 21466304
    dealloc 128 byte(s) at 21466160

    不管哪种开法,都是&vec[0]小包不行大包可以, arr 全都可以。
    aigebiu
        28
    aigebiu  
    OP
       2016-05-06 11:44:32 +08:00
    新发现,如果静态分配 vector 的内存,问题就解决了;如果用 malloc 申请内存,问题也解决了。
    看来问题要变成 new 和 malloc 的区别了 233
    除了调 constructor 外还有什么区别呢?请教楼上诸位了

    输出:
    (static) alloc 29 byte(s) at 140733249528912
    &vec[0]: 140733249528912, buffer: 140733250577904
    dealloc 29 byte(s) at 140733249528912

    malloc 29 byte(s) at 35519328
    &vec[0]: 35519328, buffer: 140731255293104
    free 29 byte(s) at 35519328

    就不传 patch 了,静态的大概是这样:
    value_type pool[1<<20];
    size_type cur = 0;
    pointer allocate(size_type num, const void* = 0)
    {
    pointer p = &pool[cur];
    cur += num;
    std::cerr << "(static) alloc " << num << " byte(s) at " << (size_type)p << std::endl;
    return p;
    }

    动态( malloc )的大概是这样:
    pointer allocate(size_type num, const void* = 0)
    {
    T* p = (T*)(malloc(num * sizeof(T)));
    std::cerr << "malloc " << num << " byte(s) at " << (size_type)p << std::endl;
    return p;
    }
    void deallocate(pointer p, size_type num)
    {
    std::cerr << "free " << num << " byte(s) at " << (size_type)p << std::endl;
    free((void*)p);
    }
    3dwelcome
        29
    3dwelcome  
       2016-05-06 11:49:22 +08:00 via Android
    很负责的告诉楼主、 vector 内存是连续的、不管那个 stl 实现都是如此。

    你的问题最大可能性、是预留空间不足。库里都是指针、没办法帮你修正 vector 数组大小。而你程序外部的修正、很可能把库里记录的地址给冲掉了。
    araraloren
        30
    araraloren  
       2016-05-06 11:56:36 +08:00
    @aigebiu `gcc`的`stl`实现中`vector`本身默认的`allocator`就是简单的`new`操作,而对于内置类型`uint_8`并没有什么构造区别
    aigebiu
        31
    aigebiu  
    OP
       2016-05-06 12:02:19 +08:00
    @araraloren 是,内置类型没有构造一说,所以我在考虑“调 constructor 外还有什么区别”。 27 、 28 楼里提到,在自己实现的 allocator 中,用 new 就有问题,用 malloc 就成功了,说明他俩分到的内存是有区别的。
    aigebiu
        32
    aigebiu  
    OP
       2016-05-06 12:04:47 +08:00
    @3dwelcome 能否详细讲下预留空间不足的问题?为什么同样是小空间,用 malloc 给 vector 开的内存就没问题呢?
    aigebiu
        33
    aigebiu  
    OP
       2016-05-06 12:52:38 +08:00
    @aigebiu
    @araraloren
    @3dwelcome 抱歉 刚才仔细看了下 malloc 也有问题,只有静态分配的没问题。(开了个 pool 数组的那个)
    @zwzmzd 这样看来,可能确实和堆/栈有关。
    araraloren
        34
    araraloren  
       2016-05-06 14:08:48 +08:00
    @aigebiu 那就是了,因为`new`的实现也是简单的调用`malloc`,并没有什么太高级的东西。。
    还有我再次想说的是,不要那么使用`vector`,自己实现一个简单的指针资源管理都比直接用`vector`强。。
    aigebiu
        35
    aigebiu  
    OP
       2016-05-06 14:34:28 +08:00
    @araraloren 嗯是 要不是因为它提供的接口是 vector 我也不愿意这么折腾 自己 malloc free 最方便 谢谢提醒
    neoblackcap
        36
    neoblackcap  
       2016-05-06 14:41:01 +08:00
    @aigebiu 返回 vector 这样的容器很好啊。你 vec 变量生命周期完了那么久自己释放资源了,你 malloc 跟 free 还有可能忘记了。
    yuyang1110
        37
    yuyang1110  
       2016-05-06 18:23:45 +08:00
    @aigebiu 跑个题,其实可以用 vector.data() http://en.cppreference.com/w/cpp/container/vector/data
    linux40
        38
    linux40  
       2016-05-07 09:46:41 +08:00 via Android
    new 并不是简单的 malloc ,这样认为的,自己回去补充知识吧。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     901 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 20:16 PVG 04:16 LAX 13:16 JFK 16:16
    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