【问题标题】:why does the code output -1 in place of 1为什么代码输出 -1 代替 1
【发布时间】:2011-05-01 09:02:11
【问题描述】:
int main ()  
{  
struct bit{  
 char f1:1;  
 char f2:1;  
};  
struct bit b;  
b.f1= 0x1;   
b.f2 = 0x1;   
printf("%d\n",b.f1);  
return 0;  
} 

使用 gcc 编译,代码输出 -1。不应该是1吗?是因为我在一个小端机器上编译吗?

添加:在使用 GDB 进行调试时,我看到初始化结构成员后的值为 -1。即打印前为-1。以下是 GDB 的打印输出:
(gdb) p b
$7 = {f1 = -1 '', f2 = -1 ''}

如果您需要更多调试命令,请告诉我。请提供执行此操作的命令。

【问题讨论】:

  • 将%d 更改为%u 看看会发生什么。请记住,当使用单个位字段时,答案是真或假,0 或非零。哦,在粘贴代码时考虑使用代码格式化程序(1 和 0 按钮)。

标签: c


【解决方案1】:

位域应该是通常是'unsigned xxxxx'。

[编辑]
原因正是您遇到的;有人以“不寻常”的方式使用位域,得到了他们不应该得到的结果。

位域的意义在于反映一点。有点什么价值观? 0 和 1(我只是在说普通位,我无法理解量子的东西)。 然而,您已经找到了一种将 -1、0、1 插入到同一个字段中的方法。它必须在某个地方中断。我相信很多位域混淆会导致负位域,因此无符号修饰符可以缓解这种混淆。

当您将位域定义为 int 时,您可以有负值。这就是上述结果的原因。
此外,请参阅 here 了解有关此主题的更详细信息。请注意,这已经在 SO 上重复了无数次,因此搜索“位域无符号”将证明非常有启发性
[/编辑]

对于“R.”,回复:“签名......是实现定义的,但它不能因一个元素而异。”

#include <stdio.h>
int main ()
{
   struct bit{
      char f1:1;
      unsigned char f2:1;
   };
  struct bit b;
  b.f1 = 1;
  b.f2 = 1;
  printf("%d\n",b.f1);
  printf("%d\n",b.f2);
  return 0;
}

产生,作为输出:

-1
1

【讨论】:

  • 由于我的回答中所述的原因,是的。
  • -1 不,他没有在位域中插入-1、0、1,他的位域仅代表2个值-1和0。不多也不少。在另一个编译器上,他可能得到 0 和 1。这是编译器 char 的选择是 signed 或 unsigned。这就是为什么人们在使用charvariables 进行索引时应该小心的原因,因为它们可能会签署扩展代码和中断代码(例如,gnu C 会发出警告,例如在使用isalpha(c) 之类的东西时)。
  • 我不这么认为。也许对于这个位域,他得到 -1 和 0,但对于同一个结构中的另一个,他可能得到 1 和 0。我仍然认为,为了清楚起见,0 和 1 优于 0 和 -1。跨度>
  • 它只是依赖于所用类型的签名,没有别的。在某些情况下,其中一个或另一个是更合乎逻辑的事情。也许他想将它与 VB 接口,其中布尔值为 0 和 -1。顺便说一句:在 1 补码机器上,他会得到 0 和 -0。
  • @tristopia - 同意,实现依赖通常是这里的问题。尽管标准说允许有符号/无符号和布尔值,但它们往往会使人们混淆视听者,因此我的原始声明(我错了:标准,我是正确的:可用性和清晰度)
【解决方案2】:

char 可以是unsigned 或signed 类型,由编译器决定。在您的情况下,它显然是signed,因此当您打印位字段时,编译器会将符号扩展到您的位扩展到的int 的大小。在 2 补码表示中,不应忘记 -1 表示为每个位集 11111111 11111111 11111111 11111111 在 32 位整数上为 -1。当您只有 1 位时,您只能表示 2 个补码中的 2 个值:0 和 1,这是 0 和 -1 的二进制表示。

编辑:这里是 C 标准的实际部分 第 6.2.5 章第 15 节:char、signed char 和 unsigned char 这三种类型统称为字符类型。实现应将 char 定义为与有符号字符或无符号字符具有相同的范围、表示和行为。35

35) limits.h 中定义的 CHAR_MIN 将具有值 0 或 SCHAR_MIN 之一,这可用于区分这两个选项。无论做出何种选择,char 都是与其他两种不同的类型,并且与任何一种都不兼容。

【讨论】:

  • 好答案。这是有道理的(我确实在这些方面考虑过,但对打印 1 的不同机器感到困惑。两台机器的区别在于字节顺序,并且陷入其中,而不是考虑 C 基础知识。愚蠢的我!)。要分配正 1,我必须将其显式声明为无符号字符。据我所知,'char' 始终被视为已签名而不是未签名。这个实现是依赖的还是标准的?如果它是一个标准,我可能会通知编译器开发人员该错误。非常感谢您为此付出的时间和精力。
  • 打印 1 的编译器是 C89 兼容的,而不是 C99 兼容的
  • @sandeep:错了。普通的char 是有符号还是无符号(或者更重要的是,char 类型的位域的行为)是实现定义的。 C89 和 C99 都同意这一点。
【解决方案3】:

添加到 Skizz 和 tristopia 的正确答案:对于 C99,现代 C 的位字段应该是 signed 或 unsigned int 类型或 bool 类型(又名 _Bool)。某些平台可能允许所有其他类型,但不一定是可移植的。更糟糕的是,即使您将它们指定为纯 int,结果也可能是有符号或无符号的。所以如果你只需要一个标志,最好坚持bool,如果你需要不止一个位,最好坚持unsigned。

【讨论】:

    【解决方案4】:

    上面的解释很棒。 为了使您的程序正常工作,一种可能的解决方案可能是使位字段为 unsigned int 类型。

    【讨论】:

      【解决方案5】:

      我相信 KevinDTimm 是正确的 - 位字段通常是 ​​unsigned name:x。

      这就是我认为正在发生的事情:处理器从字段的第一位确定符号。 gcc 字符已签名(有一个unsigned char,因此您的代码实际上是:

      struct bit 
      {
         char f1;
         char f2;
      };
      

      每个字符为一个字节(4 位),最高字节表示符号(1=neg,0=pos)。但是由于您的字段是一位,所以这是符号位。你把它设置为负数。

      希望这会有所帮助。如有错误请评论,我会改正的。

      【讨论】:

      • 不,位域可以有符号或无符号,这取决于你想做什么,没有规则。
      【解决方案6】:

      您的位域是一位宽并且是有符号值。最高位通常表示值的符号,因此,将 1 设置为一位宽的有符号值会设置符号位,以便读取该值会得到 -1。

      【讨论】:

        【解决方案7】:

        我不太了解 C。例如,我不知道:1 在char f1:1 中的作用,但我可以通过删除该代码在我的电脑上运行:

        #include <stdio.h>
        
        int main ()
        {
         struct bit{
          char f1;
          char f2;
         };
        
         struct bit b;
        
         b.f1 = 0x1;
         b.f2 = 0x1;
         printf("%d\n",b.f1);
         return 0;
        }
        

        下面的输出:

        chooper@brooklyn:~/test$ gcc -o foo foo.c
        chooper@brooklyn:~/test$ ./foo
        1
        

        希望对你有帮助!

        【讨论】:

        • @Charles Hooper。感谢您的努力。我能够在不同的机器上获得相同的输出。我调查了一下,发现大端机器输出1,小端机器输出-1。我用来测试字节序的C sn-p如下: int a=1;字符 *p = (字符 *)&a; if (*p==1) printf("小端");否则 printf("大端");你能在你的机器上测试它并检查它的字节顺序吗?再次感谢您的努力
        • 很深,它与字节序无关,它实际上取决于编译器的选择来考虑char 有符号或无符号。有些编译器甚至有一个命令行选项可供选择(如果我没记错的话是微软编译器)。
        • 我在一个小端机器上,出于某种完全让我忘记的原因
        • 所以这证明(正如@tristopia 在他上面的回答中所说的那样)这取决于编译器如何处理'char'-有符号或无符号。但我认为除非明确指定'char' 表示'signed char'。由于某种原因,我使用的其中一台机器上的编译器将其视为无符号(输出 1 且不是 gcc 的机器)。我想这应该是一个编译器错误。顺便问一下Charles,你是用哪个编译器编译的?
        • 这就是标准C99对char的说法:char、signed char和unsigned char这三种类型统称为字符类型。实现应将char 定义为与signed char 或unsigned char 具有相同的范围、表示和行为。
        猜你喜欢
        • 2012-09-21
        • 1970-01-01
        • 2022-06-27
        • 2021-10-28
        • 2021-12-15
        • 1970-01-01
        • 1970-01-01
        • 2017-03-02
        • 2020-03-14
        相关资源
        最近更新 更多