对空结构体求 sizeof

标签: std C CPlusPlus sizeof | 发表时间:2011-10-07 17:15 | 作者:Neuron Teckid xcv58
出处:http://blog.bitfoc.us

    C++ 声称完全兼容了 C, 这一点在某些细节上不尽然. 比如对空结构体 --- 没有成员, 不含虚函数, 虽然 C 还生活在没有虚函数的三叠纪 --- 求 sizeof 的结果. 具体地说就是下面这个表达式

struct empty {};

sizeof(emtpy);

    在 C 和 C++ 中会得到不同的值: C 中其值为 0 (在主流编译器中如此), 而 C++ 中其值为 1. 这个微妙的不同步源于 C 中的一个指针相减问题, 如下代码

#include <stdio.h>

struct empty {};

int main()
{
    struct empty x;
    struct empty y;
    printf("%ld", &x - &y);
    return 0;
}

    以 C 语言编译并运行, 程序会直接崩溃掉, 因为在 C 中计算表达式 &x - &y 的值等同于 ((char*)&x) - ((char*)&y) / sizeof(struct empty). 这个整数除法非常糟糕, 毫无疑问 C 编译器应该了解到危险所在了: 在编译期, 它应该发现该除法算式的常数分母是整数 0, 但是它还是义无反顾地生成了代码, 甚至连警告也不给, 将程序推入运行时再来崩溃大奖的深渊.
    本来这种事情应该偷偷改掉拉倒, 可是 C 标准对这个事情讳而不谈, 丢出一张王牌 "对空结构体或联合求 sizeof 将会是未定义行为". 对此 C++ 只好吐了个槽, 说任何对象至少要占用 1 字节空间. 所以其实 C++ 标准也没有明确说出 "对空结构体或联合求 sizeof 将会是 1" 这样的话, 但是根据前面这个规定, 由编译器厂商演绎出来的结果就是这样的, sizeof 纷纷得到结果 1, 包括下面这样的情况

struct empty_base_a {};
struct empty_base_b {};
struct empty_inherit : empty_base_a, empty_base_b {};

sizeof(empty_inherit); /* 1 */

    即对从空类上 (多重) 继承的空子类求 sizeof 也将得到 1.
    这一招看起来很挫, 但还真的管用了, 用 C++ 编译器编译并运行上述程序, 零也不除了, 程序也不会崩了, 还能给出正确地结果.

    虽然把两个什么空的东西用继承的方式捏在一起不会产生体积变大, 但是一个数组的什么空的东西则会导致体积累加, 如

struct empty {};

int main()
{
    struct empty x[4];
    printf("%ld", sizeof(x)); /* 4 */
    return 0;
}

    这段 C++ 代码的运行结果将是 4, 也就是 x 占用了 4 个 1 字节. 这又扯到 C++ 另一个核心编程思维 --- 面向迭代器. 例如下面一坨代码

struct empty {};

void echo(empty)
{
    std::cout << "echo" << std::endl;
}

int main()
{
    struct empty x[4];
    std::for_each(x, x + 4, echo);
    return 0;
}

    如果认为整个数组是一个对象, 打个包求 sizeof 才能得到 1, 而 x[0]x[4] 等等有相同的地址, 那么 std::for_each 中的循环将一次也不被执行. 类似的, 让多个空类对象聚合在一个空类对象中时, 它们占用的空间大小是会累加的, 如

struct empty {};
struct twin {
    empty a;
    empty b;
};

sizeof(twin); /* 2 */

相关 [结构 sizeof] 推荐:

对空结构体求 sizeof

- xcv58 - Bit Focus
    C++ 声称完全兼容了 C, 这一点在某些细节上不尽然. 比如对空结构体 --- 没有成员, 不含虚函数, 虽然 C 还生活在没有虚函数的三叠纪 --- 求 sizeof 的结果.     在 C 和 C++ 中会得到不同的值: C 中其值为 0 (在主流编译器中如此), 而 C++ 中其值为 1.

C/C++刁钻问题各个击破之细说sizeof

- d0ngd0ng - C++博客-首页原创精华区
Sizeof的作用非常简单:求对象或者类型的大小. 然而sizeof又非常复杂,它涉及到很多特殊情况,本篇把这些情况分门别类,总结出了sizeof的10个特性:. (0)sizeof是运算符,不是函数;. (1)sizeof不能求得void类型的长度;. (2)sizeof能求得void类型的指针的长度;.

Java Cache-EHCache系列之计算实例占用的内存大小(SizeOf引擎)

- - BlogJava-首页技术区
计算一个实例内存占用大小思路. 在Java中,除了基本类型,其他所有通过字段包含其他实例的关系都是引用关系,因而我们不能直接计算该实例占用的内存大小,而是要递归的计算其所有字段占用的内存大小的和. 在Java中,我们可以将所有这些通过字段引用简单的看成一种树状结构,这样就可以遍历这棵树,计算每个节点占用的内存大小,所有这些节点占用的内存大小的总和就当前实例占用的内存大小,遍历的算法有:先序遍历、中序遍历、后序遍历、层级遍历等.

JBPM表结构

- - CSDN博客综合推荐文章
      JBPM全称——Java  Business PrcessManagerment(业务流程管理),它是覆盖了业务流程管理、工作流、服务协作等领域的一个开放的、灵活的、易扩展的可执行流程语言框架.        (1)它的业务逻辑定义没有采用目前的一些规范,而是采用了它自己定义的Jboss Jbpm Process Definition Language(jpdl).

Linux 文件结构

- Shiina Luce - OSMSG
想了解 Linux 文件系统树形结构,却又不愿翻阅 FHS 的朋友,可以参考 skill2die4 制作的这张简图. 此图算是 FHS 的图形化版本,简要的说明了 Linux 系统中各个目录的用途及层级关系,适合初学者使用参考. 不过其中较新的如 /run 目录并未在其中出现. 做为参考,这是 Fedora 16 Beta i686 上的文件结构:.

TCP报文结构

- - 互联网 - ITeye博客
一、TCP报文结构如下:.  固定首部长度为20字节,可变部分0~40字节,各字段解释:. source port number:源端口,16bits,范围0~65525. target port number:目的端口,16bits,范围同上. sequence number:数据序号,32bits,TCP 连接中传送的数据流中的每一个字节都编上一个序号.

Linux 文件结构 — LinuxTOY

- oak - linuxtoy.org
想了解 Linux 文件系统树形结构,却又不愿翻阅 FHS 的朋友,可以参考 skill2die4 制作的这张简图. 此图算是 FHS 的图形化版本,简要的说明了 Linux 系统中各个目录的用途及层级关系,适合初学者使用参考. 不过其中较新的如 /run 目录并未在其中出现.

JVM学习 - 体系结构

- - CSDN博客推荐文章
一:Java技术体系模块图. 二:JVM运行时内存区域模型. 也称"永久代” 、“非堆”,  它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域. 可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小. 运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中.

WordPress表结构说明

- - CSDN博客推荐文章
WordPress一共有以下11个表. 这里加上了默认的表前缀 wp_. wp_commentmeta:存储评论的元数据. wp_comments:存储评论. wp_links:存储友情链接(Girl is coding). wp_options:存储WordPress系统选项和插件、主题配置. wp_postmeta:存储文章(包括页面、上传文件、修订)的元数据.