【问题标题】:Can sizeof return 0 (zero)sizeof能否返回0(零)
【发布时间】:2011-02-07 14:08:35
【问题描述】:

sizeof 运算符在 C 或 C++ 中是否可能返回 0(零)?如果可能,从标准的角度来看是否正确?

【问题讨论】:

  • 我遇到了这个:msdn.microsoft.com/en-us/library/4s7x1k91(VS.71).aspx 声明 sizeof 永远不能返回 0 但我不确定这是否只是 Microsoft 实现约束(并且似乎是 C++ 特定的)。
  • 我想知道抽象基类的大小是否可以是0(不可能声明它们的实例,这样反对就不会适用)
  • 如果您需要 C++ 中结构的真实大小(以区分真正的空结构与其中包含 char 的结构),您可以使用 std::is_empty_v<T> ? 0 : sizeof(T); 打败 sizeof 的谎言。

标签: c++ c sizeof


【解决方案1】:

在 C++ 中,空类或结构的 sizeof 定义至少为 1。来自 C++ 标准,9/3“类”:“类类型的完整对象和成员子对象应具有非零大小。”

在 C 中不允许使用空结构,除非通过扩展(或编译器中的缺陷)。

这是语法的结果(要求大括号内有东西)以及 6.7.2.1/7 “结构和联合说明符”中的这句话:“如果 struct-declaration-list 不包含命名成员,行为未定义”。

如果允许使用大小为零的结构,那么它就是语言扩展(或编译器中的缺陷)。例如,在 GCC 中,扩展名记录在 "Structures with No Members" 中,上面写着:

GCC 允许 C 结构没有成员:

 struct empty {
 };

结构的大小为零。在 C++ 中,空结构是语言的一部分。 G++ 将空结构视为具有char 类型的单个成员。

【讨论】:

  • 在 C 中是不允许还是未定义?
  • @TheJuice:语法要求结构定义中包含某些内容,但我认为您可能只需一个分号就可以逃脱(语法方面)(我不确定关于这一点 - 阅读 BNF 不是我的强项之一)。但是,如果您在结构定义中没有任何具有名称的东西,那么您就处于未定义的行为领域(这实际上对您没有任何好处 - 您不能指望它做任何明智的事情)。
  • @Michael Burr:实际上,语法本身已经禁止空结构。 C 中的空结构立即是语法错误,而不是 UB。关于“无名成员 - UB”的评论是为了填补另一个漏洞。您可以声明struct { int : 1 }(即一个未命名的位域)以正式满足语法要求。为了取缔这样的事情,那里有额外的评论。
  • @Andrey:我明白了,C99 §6.7.2.1/4 “位域的类型应该是 _Bool、signed int、unsigned int 或其他一些实现的合格或不合格版本- 定义的类型。”另外,我刚刚测试了int:1 方式,GCC 和 G++ 确实将其设为 1。对此感到抱歉。
  • gcc 确实为空结构的 sizeof 返回 0;int main(){ printf("%i",sizeof(struct {})); }
【解决方案2】:

sizeof 在 C 和 C++ 中从不返回 0。每次您看到 sizeof 评估为 0 时,它都是与语言无关的特定编译器的错误/故障/扩展。

【讨论】:

  • 参考或者它没有发生。
  • @MK:语言标准是参考。那里没有一个地方。这一切都源于这样的事实,即根据定义,任何对象类型在 C 或 C++ 中都具有非零大小。您不能将sizeof 应用于不完整类型这一事实也起到了一定的作用。以此类推。
  • 那么,就投票而言,stackoverflow 哪个问题的排名应该更高:+8/-0 还是 +19/-11?一个是有争议的,另一个是共识,轻推。
  • GCC 4.5.x 在类声明中使用 sizeof( ) 时返回零。这对我来说很有意义,因为当时它无法知道班级的规模。
  • @everclear:首先,这并不完全准确。 “在类的声明中”有些地方应该将类视为完整类型。例如,这包括默认参数。如果您的 GCC 版本在这种情况下从 sizeof 返回零,那么它就被彻底破坏和无用了。
【解决方案3】:

C 中的每个对象都必须有一个唯一的地址。换一种说法,一个地址必须不超过一个给定类型的对象(为了使指针解除引用工作)。话虽如此,考虑一个“空”结构:

struct emptyStruct {};

更具体地说,是它们的数组:

struct emptyStruct array[10];
struct emptyStruct* ptr = &array[0];

如果对象确实是空的(即如果sizeof(struct emptyStruct) == 0),那么ptr++ ==> (void*)ptr + sizeof(struct emptyStruct) ==> ptr,这没有意义。然后*ptr 将引用哪个对象,ptr[0]ptr[1]

即使结构没有内容,编译器也应该将其视为长度为一个字节,以保持“一个地址,一个对象”的原则。

C 语言规范(A7.4.8 节)将此要求表述为

当应用于结构或联合时, 结果(sizeof 运算符) 是对象中的字节数, 包括制作所需的任何填充物 对象平铺数组

由于必须将填充字节添加到“空”对象才能使其在数组中工作,因此sizeof() 必须为任何有效输入返回至少 1 的值。

编辑: C 规范的 A8.3 节将没有成员列表的结构称为不完整类型sizeof 的定义特别指出(强调添加):

运算符(sizeof)可能不是 应用于函数的操作数 类型,或类型不完整,或 位域。

这意味着在空结构上使用sizeof 与在尚未定义的数据类型上使用它一样无效。如果您的编译器允许使用空结构,请注意,根据 C 规范,不允许在它们上使用 sizeof。如果您的编译器无论如何都允许您这样做,请了解这是非标准行为,不适用于所有编译器;不要依赖这种行为。

编辑:另请参阅 Bjarne Stroustrup 的常见问题解答中的 this entry

【讨论】:

  • 这个逻辑就是为什么 C++ 要求 sizeof 从不返回 0。但根据 Michael Burr 的回答,C 中的空结构是未定义的行为。这可能解释了为什么 gcc 的空结构实现违反了您的所有假设。
  • @bta:你所说的可能适用于 C++。在 C 中,空结构(如您的示例)只是违反约束,是语法错误。所以形式上,“空结构的大小”问题在 C 中甚至不存在。
  • @AndreyT:我意识到空结构在 C 中是无效的,我只是想表明即使你可以做到,sizeof 的定义仍然会返回非零值。
  • @bta 措辞优美。你应该补充一点,C 选择通过完全禁止空结构来解决这个问题,而 C++ 选择通过将它们定义为至少 1 的大小来解决这个问题。
  • @bta: gcc 选择允许 C 中的空结构,并分配它们的大小为零。
【解决方案4】:

空结构,如isbadawimentions。 gcc 也允许arrays of 0 size:

int a[0];
sizeof(a);

编辑:看到 MSDN 链接后,我尝试了 VS2005 中的空结构,但 sizeof 确实返回了 1。我不确定这是否是 VS 错误,或者规范是否对这类事情具有某种灵活性

【讨论】:

  • 这是错误的。在@isbadawi 的回答中查看@AndreyT 的cmets。
  • 仅仅因为 GCC 允许它并不能使它成为合法的 C/C++。
  • @JohnDibling。你说的对。但是在越来越多的编译器允许它(也是clang)之后,它可能有一天会变得合法。在编码时,我不会打赌。
  • @PatrickFromberg:只是一个观察。我在 9 年前写了那条评论。就个人而言,我支持(并且一直支持)不让编译器告诉我什么是合法的,什么是不合法的。
【解决方案5】:

在我看来,对于大小为 0 的结构,sizeof 最好返回 0(本着 c 的精神)。 但是程序员在获取空结构的大小时必须小心。

但它可能会导致问题。 当定义了此类结构的数组时,则

&arr[1] == &arr[2] == &arr[0]

这使他们失去了身份。

我想这并不能直接回答您的问题,无论是否可能。 好吧,这可能取决于编译器。 (如上面迈克尔的回答中所说)。

【讨论】:

  • 这里不是见仁见智的问题。这里有一个拥有最终决定权的权威——标准——问题是关于sizeof为零的标准所说的。
  • @wilhelmtell,常识有可能超越任何标准。
  • @Joshua:你的编译器有可能超越你的常识。如果您完全关心您的代码在多个编译器中的功能,那么最好的办法就是忘记您的常识告诉您的内容并坚持标准。
  • @Joshua 有时标准委员会会做出看起来很糟糕的决定。是的我同意。但是,如果我们真的想陷入徒劳无休的自以为是的讨论,那么这个决定并不是那些糟糕的决定之一。 sizeof() 一个空的struct 不为零的原因是,标准可以保证没有两个不同的空struct 实例化(或两个不同的空structs)共享相同的内存地址。特别是,这保证了对 operator new() 的调用将返回一个有效的、唯一的内存地址(否则完全失败)。
  • @Joshua 现在,如果讨论是关于我们是否应该听标准,那么这是另一种徒劳。我们可以同意我们应该遵循标准,同意不同意,或者同意我们不应该遵循标准并期望迅猛龙过来并自发地咬掉我们的四肢的那种。事实上,当我们这样做的时候,为什么我们不同意说一种共同的语言呢?不同意将我们的理由依赖于共同的逻辑,或者有共同的道德规范?当然,有更好的方法来获得进步和秩序。或者,谁还需要这些?
【解决方案6】:
typedef struct {
  int : 0;
} x;

x x1;
x x2;

在 MSVC 2010 (/Za /Wall) 下:

sizeof(x) == 4
&x1 != &x2

在 GCC 下(-ansi -pedantic -Wall):

sizeof(x) == 0
&x1 != &x2

即即使在 GCC 下它的大小为零,结构的实例也有不同的地址。

ANSI C(C89 和 C99 - 我没有看过 C++)说“应该可以唯一地表达对象的每个单独字节的地址。”这在大小为零的对象的情况下似乎不明确,因为它可能没有字节。

编辑:“没有声明符但只有冒号和宽度的位域声明表示未命名的位域。作为这种情况的特殊情况,宽度为 0 的位域表示没有进一步位域将被打包到放置前一个位域(如果有的话)的单元中。"

【讨论】:

    【解决方案7】:

    我认为它永远不会在 c 中返回 0,不允许空结构

    【讨论】:

      【解决方案8】:

      这是一个测试,其中 sizeof 产生 0

      #include <stdio.h>
      
      void func(int i)
      {
              int vla[i];
              printf ("%u\n",(unsigned)sizeof vla);
      }
      
      
      int main(void)
      {
      
              func(0);
              return 0;
      }
      

      【讨论】:

      • 我可能读错了,但我相信 C 中的 VLA 的大小必须大于零。 “如果 size 是一个不是整数常量表达式的表达式:...否则,每次对其求值时,它的值都应大于零。”
      • 可能,但是 gcc 会产生 0 - 所以“有可能” - 虽然可能不正确。
      【解决方案9】:

      如果你有这个:

      struct Foo {}; 
      struct Bar { Foo v[]; }
      

      g++ -ansi 返回 sizeof(Bar) == 0。clang & intel 编译器也是如此。

      但是,这不能用 gcc 编译。我推断它是一个 C++ 扩展。

      【讨论】:

        【解决方案10】:
        struct Empty {
        } em;
        
        struct Zero {
            Empty a[0];
        } zr;
        
        printf("em=%d\n", sizeof(em));
        printf("zr=%d\n", sizeof(zr));
        

        结果:

        em=1
        zr=0
        

        【讨论】:

        • 这会在我的系统上打印em=1zr=1
        猜你喜欢
        • 2012-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-21
        • 1970-01-01
        • 2017-09-05
        • 2014-12-02
        相关资源
        最近更新 更多