【问题标题】:sizeof(bitfield_type) legal in ANSI C?sizeof(bitfield_type) 在 ANSI C 中合法吗?
【发布时间】:2010-09-10 00:36:47
【问题描述】:
struct foo { unsigned x:1; } f;
printf("%d\n", (int)sizeof(f.x = 1));

预期的输出是什么,为什么?不允许直接取位域左值的大小。但是通过使用赋值运算符,我们似乎仍然可以获取位域类型的大小。

什么是“位域的字节大小”?它是保存位域的存储单元的大小吗?是bf占用的比特数四舍五入到最接近的字节数吗?

或者是构造未定义的行为,因为标准中没有任何内容可以回答上述问题?同一平台上的多个编译器给我的结果不一致。

【问题讨论】:

    标签: c


    【解决方案1】:

    不会

    (f.x = 1)
    

    是一个计算结果为真的表达式(技术上计算为赋值的结果,在这种情况下为 1/真),因此,

    sizeof( f.x = 1)
    

    根据存储它需要多少个字符来询问 true 的大小?

    我还应该补充一点,sizeof 上的维基百科文章很好。特别是,他们说“sizeof 是一个编译时运算符,它返回它前面的变量或带括号的类型说明符的大小,以 char 大小的倍数表示。”

    文章还解释了 sizeof 对表达式起作用。

    【讨论】:

    • f.x = 1 计算结果为 f.x,不正确。
    • 在这个例子中我们都是对的。 1 在 C 中是 true 的值 :) 但是,是的,我应该编辑帖子以反映那个小想法。
    • 您将类型与值混淆了。该值与 sizeof 的结果无关。 sizeof(f.x = 0) 将给出相同的结果;重要的是类型,而不是价值。
    • 我其实很清楚类型和值之间的区别。 sizeof 的一个有趣的“功能”是您可以询问值的大小。顺便问一下,上面程序中 1 的类型是什么?当 C 必须猜测时,它会猜测 int,但也许它不需要猜测?
    • Jason,你为什么不能承认你的帖子不正确或对问题没有回应,而不是防御呢?一开始它被正确地修改了(不是我)如果你必须问表达式的结果类型是什么,那么你没有阅读讨论甚至原始问题。
    【解决方案2】:
    sizeof( f.x = 1)
    

    返回 1 作为其答案。 sizeof(1) 可能是您正在编译的平台上的整数大小,可能是 4 或 8 个字节。

    【讨论】:

      【解决方案3】:

      不,您一定想到了 == 运算符,它在 C 中生成 int 类型的“布尔”表达式,在 C++ 中确实是 bool。

      我认为表达式会将值 1 转换为对应的位域类型并将其分配给位域。结果也应该是位域类型,因为我看不到隐藏的促销或转换。

      因此我们可以有效地访问位域类型。

      不需要编译器诊断,因为“f.x = 1”不是左值,即它不直接指定位域。它只是一个“无符号:1”类型的值。

      我专门使用“f.x = 1”,因为“sizeof f.x”采用位域左值的大小,这显然是不允许的。

      【讨论】:

        【解决方案4】:

        如您所见,试图获取位域的大小是不合法的。 (sizeof 以字节为单位返回大小,这对于位域没有多大意义。)

        sizeof(f.x = 1) 将返回表达式类型的大小。由于 C 没有真正的“位域类型”,因此表达式(此处:赋值表达式)通常获取位域的基本类型的类型,在您的示例中为 unsigned int,但编译器可以使用内部较小的类型(在这种情况下可能是unsigned char,因为它对于一位来说足够大了)。

        【讨论】:

          【解决方案5】:

          (f.x = 1)
          

          不是表达式,它是赋值,因此返回赋值。在这种情况下,该值的大小取决于已分配给它的变量。

          unsigned x:1
          

          有 1 位,它的 sizeof 返回 1 字节(8 位对齐)

          如果你会使用

          unsigned x:12
          

          那么 sizeof(f.x = 1) 将返回 2 字节(同样是因为 8 位对齐)

          【讨论】:

            【解决方案6】:

            sizeof(1) 大概是您正在编译的平台上的整数大小,可能是 4 或 8 个字节。

            请注意,我没有采用 sizeof(1),它实际上是 sizeof(int)。仔细看,我取的是 sizeof(f.x = 1),实际上应该是 sizeof(bitfield_type)。

            我希望看到能告诉我构造是否合法的引用。作为额外的奖励,如果它告诉我预期什么样的结果会很好。

            gcc 肯定不同意 sizeof(bitfield_type) 应该与 sizeof(int) 相同的断言,但仅限于某些平台。

            【讨论】:

              【解决方案7】:

              如您所见,试图获取位域的大小是不合法的。 (sizeof 以字节为单位返回大小,这对于位域没有多大意义。)

              那么你是说行为是未定义的,即它与“*(int *)0 = 0;”具有相同程度的合法性,编译器可以选择不明智地处理这个问题?

              这就是我想要找出的。您是否认为它是由于遗漏而未定义,还是有什么明确声明它是非法的?

              【讨论】:

                【解决方案8】:

                不是表达式,它是一个赋值,因此返回赋值。在这种情况下,该值的大小取决于已分配给它的变量。

                首先,它一个包含赋值运算符的表达式。

                其次,我很清楚我的示例中发生了什么:)

                那么 sizeof(f.x = 1) 将返回 2 字节(同样是因为 8 位对齐)

                你从哪里得到的?这是在您尝试过的特定编译器上发生的情况,还是标准中规定了这些语义?因为我还没有找到任何这样的说法。我想知道这个构造是否能保证工作。

                【讨论】:

                  【解决方案9】:

                  在第二个示例中,如果您将结构定义为

                  struct foo { unsigned x:12} f;
                  

                  然后将类似 1 的值写入 f.x - 由于对齐,它使用 2 个字节。如果你做这样的任务

                  f.x = 1;
                  

                  这将返回分配的值。这与

                  非常相似
                  int a, b, c;
                  a = b = c = 1;
                  

                  从右到左评估分配的位置。 c = 1 将 1 分配给变量 c,并且此分配返回分配的值并将其分配给 b(依此类推),直到将 1 分配给 a

                  等于

                  a = ( b = ( c = 1 ) )
                  

                  在您的情况下,sizeof 获取分配的大小,它不是位域,而是分配给它的变量。

                  sizeof ( f.x = 1)
                  

                  不返回位域大小,但变量赋值是 1 的 12 位表示(在我的情况下),因此 sizeof() 返回 2 字节(因为 8 位对齐)

                  【讨论】:

                    【解决方案10】:

                    听着,我完全理解我在用分配技巧做什么。

                    您告诉我位域类型的大小被四舍五入到最近的字节数,这是我在最初的问题中列出的一个选项。但是你没有用引用来支持它。

                    特别是,我尝试了各种编译器,即使我将它应用于只有一个位的位域,它们也会给我 sizeof(int) 而不是 sizeof(char)。

                    我什至不介意多个编译器随机选择他们自己对此构造的解释。当然,位域存储分配是完全由实现定义的。

                    但是,我真的很想知道该构造是否保证能够工作并产生一些价值。

                    【讨论】:

                      【解决方案11】:

                      C99 标准 (PDF of latest draft) 在第 6.5.3.4 节中提到了 sizeof 约束:

                      sizeof 运算符不得应用于具有函数类型或 不完整的类型,这种类型的括号名称,或表达式 指定位域成员。

                      这意味着允许将sizeof 应用于赋值表达式

                      6.5.16.3 说:

                      赋值表达式的类型是左操作数的类型...

                      6.3.1.1.2 说关于整数促销:

                      可以在表达式中使用 int 或 unsigned int 的任何地方都可以使用以下内容:

                      • ...
                      • _Boolintsigned intunsigned int 类型的位字段。

                      如果一个 int 可以表示原始类型的所有值, 该值被转换为int; 否则,将其转换为unsigned int

                      因此,您的测试程序应该输出int 的大小,即, sizeof(int).

                      有没有不这样的编译器?

                      【讨论】:

                        【解决方案12】:

                        CL,我以前看过你的引用,并且同意它们是完全相关的,但即使在阅读它们之后,我也不确定代码是否已定义。

                        6.3.1.1.2 说关于整数促销:

                        是的,但整数促销规则仅适用于实际执行促销的情况。我不认为我的例子需要提升。同样,如果你这样做

                        char ch;
                        sizeof ch;
                        

                        ...那么 ch 也不会被提升。

                        我认为我们在这里直接处理位域类型。

                        我还看到了 gcc 输出 1,而许多其他编译器(甚至其他 gcc 版本)却没有。这并不能让我相信代码是非法,因为大小也可以由实现定义,足以使结果在多个编译器之间不一致。

                        但是,我对代码是否可能未定义感到困惑,因为标准中似乎没有说明如何处理 sizeof 位域大小写。

                        【讨论】:

                          【解决方案13】:

                          你是对的,整数提升不适用于sizeof的操作数:

                          整数提升仅适用于:作为通常算术转换的一部分,适用于某些参数表达式,适用于一元 +、- 和 ~ 运算符的操作数,以及移位运算符的两个操作数,由它们指定相应的子条款。

                          真正的问题是位域是否有自己的类型。

                          约瑟夫迈尔斯告诉我:

                          结论 从 C90 DRs 是位域有自己的类型,而从 C99 DRs 是离开他们是否有自己的实现定义的类型,以及 GCC 遵循 C90 DR,因此分配的类型为 int:1 而不是 提升为 sizeof 的操作数。

                          这已在Defect Report #315 中讨论过。

                          总结一下:您的代码是合法的,但由实现定义。

                          【讨论】:

                            猜你喜欢
                            • 2021-08-24
                            • 2017-01-09
                            • 1970-01-01
                            • 2011-03-28
                            • 1970-01-01
                            • 1970-01-01
                            • 2012-12-17
                            • 1970-01-01
                            • 2021-12-04
                            相关资源
                            最近更新 更多