【问题标题】:Assigning negative numbers to an unsigned int not causing any error? [duplicate]将负数分配给无符号整数不会导致任何错误? [复制]
【发布时间】:2019-11-28 12:11:50
【问题描述】:

为什么编译器没有为此生成错误

浏览了一些答案,但没有得到确切的原因。 some 可以帮助我们通过一些简单的答案来理解这一点

#include <stdio.h>
int main()
{
    unsigned int a=-1;
    printf("Result :%d",a);
    return 0;
}

结果:-1

【问题讨论】:

  • 因为在无符号整数的情况下,-1 与 0xffffff 相同。没有理由抛出错误;但是会有一个警告。将 unsigned int a 赋值给 0xffffffff 并打印结果。
  • C11 H.2.2: "... C 的无符号整数类型在 LIA-1 中是“模” [与语言无关的算术] 意义在于溢出或越界结果静默换行。..."
  • 好的,那么在实时应用程序中究竟将在哪里使用有符号和无符号整数的概念
  • 如果您使用 GCC 和 -Wconversion 警告,您会收到类似 aaz59.c:4:20: error: unsigned conversion from ‘int’ to ‘unsigned int’ changes value from ‘-1’ to ‘4294967295’ [-Werror=sign-conversion] 的警告。该警告不是-Wall-Wextra 的一部分(编译器不会抱怨转换时指定了这两个但-Wconversion 没有单独和明确指定。避免所有此类警告可能很困难,这是必须明确启用它的原因之一。

标签: c int unsigned


【解决方案1】:

这是一个错误

    unsigned int a=-1;
    printf("Result :%d",a);

您不能使用"%d" 打印无符号值,您必须使用"%u"

【讨论】:

  • "%d" 是无符号和有符号的,当我们尝试使用"%u" 时输出不同,那是一些随机数..?
  • 不是随机数。在整数为 4 字节长的系统上,它将是 0xFFFFFFFF。
  • @Ganesh:转换规范%d 是用于签名的int 值——它不是用于打印unsigned int 的全部范围。
【解决方案2】:

@Ganesh,

在这种情况下编译器不会产生错误。

C 语言给你极大的力量,但你需要小心。使用“无符号”关键字告诉编译器如何处理 MSB(最高有效位)。更准确地说,当您将“int”类型的变量分配给内存位置时,它将在内存中保留一些空间(取决于平台)。为简单起见,假设 int 保留了 8 位内存,因此:1 2 3 4 5 6 7 8。每个数字代表一个字节(8 个系列)中的一个位(可以保存 0 或 1 的单个内存位置)位)。当您声明:有符号整数并输入:

signed int a = -1;

编译器会这样做:1 0 0 0 _ 0 0 0 1(_ 只是为了让数字序列更易于阅读)。

这不会被解读为一个非常大的数字。这将被读取为“负 1”,因为最高有效位(从左到右读取的第一位)将被读取为符号。这意味着有符号的 int 值仅使用 7 位(在此示例中)来存储数字的值,而一位将用于存储符号(0 表示数字为正数,1 表示数字为负数)。

“无符号”关键字有什么作用?

当你告诉编译器时:

unsigned int a = 1;

它完全改变了数字的读取方式。还记得那个记忆位置:1 2 3 4 5 6 7 8 吗?好吧,位置 1 的位将不再能够存储数字的符号。这意味着所有八位都将用于存储数字的值。

在二进制情况下:1 0 0 0 _ 0 0 0 1 作为有符号整数将被读取为 -1,但作为无符号值,它会很大(您可以使用 bin 到 dec 转换器查看确切的数字在线,但这不是重点)。

那么,您的代码有什么问题?第一个是:

(1) unsigned int a = -1;

第二个是:

(2) printf ("%d", a);

那么,我们来谈谈为什么第一条语句不会导致代码崩溃?

C 世界基于汇编指令(暂时不管它们是什么),它完全是二进制的。

C 真的不在乎数字。它们只是为了帮助程序员编写可读的程序。

当你指定-1时,不管它是什么类型的数据,内存:1 2 3 4 5 6 7 8 将被填充为:

1 2 3 4 .. 5 6 7 8
1 0 0 0 .. 0 0 0 1

故事结束。编译器按以下顺序操作:

(1) 关键字(int, float, ...):为变量预留空间 (2) 值 (-1):转换为二进制字 (3) 故事结束。

当编译器转换字面量(数字如:-1、60、2.63262 等)时,如果出现“-”符号,它将始终将 MSB 分配为:1(再次说明:MSB 为 x 2 3 4 5 6 7 8,x 位置)。在 gcc 上使用 -Wall 等编译器指令:

gcc -Wall test.c -o test.o

会给你所有的警告:“我希望得到这个,但我得到了这个”。警告可能是你最好的朋友,但我发现它们通常比受益者更邪恶。

你甚至可以说:

int a = 5.125;

这是一个浮点数,但编译器不会争论(可能会出现警告,但不会出错)。

我认为这说明了一些原因:

unsigned int a = -1;

可以通过编译器。

第二件事是“printf”。这可能是检查变量持有什么值的最糟糕的方法,因为您可以看到您将 -1 分配给无符号变量,然后将其读出为 -1。

就是这样:1 0 0 0 _ 0 0 0 1 对计算机没有任何意义。这只是一系列信息(电压的高/低值),但计算机对于其他一切都是愚蠢的。程序可以让计算机理解这些信息的含义。

当你说:printf("%d", a), using %d spaceholder,你是在说:好的,把存储在变量“a”中的信息给我。计算机得到: 1 0 0 0 _ 0 0 0 1。正如我所说,计算机对其他一切一无所知。它无法理解这些电压系列(以数字表示)的含义。

这就是 %d 出现的地方。它告诉计算机:好的,读取二进制值,其中第 1 位是符号值,并将剩余的(7 位)转换为十进制数,然后在终端上打印出来(那个黑框你会看到打印的数字/如果你使用的是 Windows,它被称为命令提示符)。

因此,%d 是一个空格符,它告诉获取的数字将被解释为符号的值。这就是为什么:

unsigned int a = -1;
>> -1

打印出 -1。

但是,如果您要使用,正如 pmg 所提到的,%u spaceholder,那么它会告诉编译器将:1 0 0 0 _ 0 0 0 1 消息视为没有任何位的数字代表一个数字的符号,因此,您会打印出类似 6316136236136 的内容(好吧,不是那么大,但您可能明白了)。

总结这一点,相当冗长,

(1) C语言中变量类型要注意,要保持一致; (2) 密切注意 printf 空间持有者,因为它们会以不同的方式解释值,例如:

float a = 5.125;
printf("%f", a);
>> 5.1250
printf("%d", a);
>> god knows what number you'll get (I just got: 1508905080)

我希望这些信息能够引导您走上正确的道路。您可以发表评论,我会尽快联系您。

【讨论】:

  • 这个答案假定负数的符号和大小表示。虽然架构完全有可能使用这种表示,但实际上您遇到的每个系统都会有负数的二进制补码表示。
  • 对于不准确(如果不是不正确)答案的文本太多了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-21
  • 2015-08-06
  • 2017-03-04
  • 2010-10-20
  • 2017-01-08
  • 1970-01-01
  • 2012-11-16
相关资源
最近更新 更多