【问题标题】:Multiple Calls to sizeof() in a Row连续多次调用 sizeof()
【发布时间】:2013-06-28 19:28:58
【问题描述】:

我有一个如下所示的结构:

struct dgm_network_pkt {
    char    responder[INET_ADDRSTRLEN];
    int     num;
    char    **neighbors;
};

num 字段跟踪已分配并由neighbors 指向的字符数组。

现在我想构造一个包含标头和结构的数据包,并发送一个包含该数据包的 UDP 数据包。为了做到这一点,我需要做很多指针运算,以及对 sizeof() 的大量调用。例如,如果我的头是enum header hdr;,我的缓冲区是char *buf;,我的结构实例是struct mystruct stc;,我需要执行以下操作(sn-p):

buf = malloc(sizeof(stc.num * SIZEOF_EACH_ARRAY) + sizeof(hdr) + sizeof(stc.responder) + sizeof(int));

memcpy(buf, &hdr, sizeof(hdr)); /* Copy over header */
memcpy(buf + sizeof(hdr), &stc.responder, sizeof(stc.responder)); /* Copy 'responder' field */
memcpy(buf + dizeof(hdr) + sizeof(stc.responder), &stc.num, sizeof(int)); /* Copy num field */

然后循环复制neighbors 指向的字符数组,再次使用相同的参数多次调用sizeof()

如果您愿意看,这里是我用来完成所有这些的功能以供参考,但没有必要回答我的问题,并且可能会增加混乱(增加了间接性)。

int dismesh_compose_net_pkt(struct dgm_network_pkt *dismesh_sta, char **buf)
{
    int i;
    int pkt_size;
    enum dgm_header dgm_hdr;

    pkt_size = dismesh_sta->num * DISMESH_ETH_ADDR_STR + sizeof(dgm_hdr) 
        + sizeof(dismesh_sta->responder) + sizeof(int);

    dgm_hdr = NETWORK_STATUS_RESP;
    if ((*buf = malloc(pkt_size)) == NULL)
        return -1; 

    memcpy(*buf, &dgm_hdr, sizeof(dgm_hdr));
    memcpy(*buf + sizeof(dgm_hdr), &dismesh_sta->responder, sizeof(dismesh_sta->responder));
    memcpy(*buf + sizeof(dgm_hdr) + sizeof(dismesh_sta->responder), &dismesh_sta->num, sizeof(int));

    /* Copy contents of neighbors to buffer */
    for (i = 0; i < dismesh_sta->num; i++) {
        memcpy(*buf + sizeof(dgm_hdr) + sizeof(dismesh_sta->responder) + sizeof(int) + i * DISMESH_ETH_ADDR_STR,
                *(dismesh_sta->neighbors + i), DISMESH_ETH_ADDR_STR);
        DGM_LOG("Packed up neighbor %d\n", i); 
    }   

    return pkt_size;
}

这里是:

我注意到我反复使用相同的参数调用sizeof():将结构的内容复制到缓冲区,每个元素的大小等等。更好的方法来做到这一点?我在想我可以在我的头文件中有一些#defines,比如说,我可以在其中执行#define OFFSET_RESPONDER sizeof(hdr)#define OFFSET_NUM OFFSET_RESPONDER + sizeof(mystruct.responder)#define SIZE_HEADER sizeof(dgm_header) 等。如果保持原样,我会注意到显着的性能损失吗?编译器(在我的情况下是 GCC)是否对此进行了优化?你认为#defines 会降低可读性吗?清理此代码的最佳方法是什么?

【问题讨论】:

  • sizeof 没有性能问题,它是编译时评估(C99 的 VLA 除外)
  • sizeof() 不能被“调用”——它不是一个函数。这是一个关键字。
  • 好吧,让我感到尴尬。在问一个答案如此明显的问题之前,我可能应该做更多的功课=/

标签: c gcc memory-management


【解决方案1】:

首先,sizeof 是一个运算符,而不是一个函数,因此编译器会将其转换为单个数字。

其次,看看offsetof宏;我认为它会完全满足您的需求。

【讨论】:

  • 很棒的附加信息! offsetof 宏会派上用场,因为这个结构只是它最终会变成的骨架,而那些 buf + sizeof(field1) + sizeof(field2) + sizeof(field3)... 很快就会变得太混乱而无法处理。再次感谢!
  • @justynnuff 不仅将各个字段大小相加很麻烦,而且由于字段之间存在填充的可能性,它也不可靠。
【解决方案2】:

sizeof() 不是函数,它在编译阶段进行评估。因此,将sizeof(something) 的多次出现更改为#define CONSTANT 应该不会改变任何性能,它只会改变编译时间但我认为它可以忽略不计。

唯一重要的方面是代码的可读性。两者都适合我,你决定。

【讨论】:

    【解决方案3】:

    您的编译器肯定会对此进行优化。我不会因为到处散布非语义的魔法标记而牺牲代码的可读性。

    您可以检查编译器输出来验证这一点,但到处都是常量。

    【讨论】:

      【解决方案4】:

      sizeof 在运行时求值的唯一情况是将其应用于可变长度数组 (VLA)。在所有其他情况下,它被替换为编译时常量。 IE。没有开销离子多次评估相同的sizeof。它在代码中看起来不太漂亮,可能会产生一些维护开销,但不会影响性能。

      将这些 sizeof 评估隐藏在宏后面不会真正改变任何性能,因为宏只是文本替换。这意味着实际代码在编译器看来将完全相同。不过,对宏进行深思熟虑的使用实际上可能会提高重复代码的可读性。

      【讨论】:

        【解决方案5】:

        sizeof 是一个内置的前缀运算符。它在编译时进行评估,除非它的参数是 C99 风格的可变长度数组。在后一种情况下,它在运行时进行评估。

        值得注意的是,sizeof 参数周围的括号仅对类型是必需的。 IE。 sizeof(dismesh_sta-&gt;responder) 可以替换为sizeof dismesh_sta-&gt;responder,这样可以节省输入一个字符。

        【讨论】:

        • @MarkoKevac 这是` ` vs ()
        猜你喜欢
        • 1970-01-01
        • 2014-05-21
        • 1970-01-01
        • 2012-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多