3-2. printf函数深入讨论

  • 学习人数 30K+
  • 适合所有人群学习
avatar
林耿亮

你好编程主讲老师

1. printf将二进制转换成字符串

printf函数从参数中获取二进制数据,并将它根据转换规范转换成字符串,并打印在控制台上。

让我们运行以下代码,并根据其结果,分析其中原理,从而理解为什么将以%开始的一串字符称作转换规范

#include <stdio.h>
int main()
{
    int aNum = 2147483647;
    unsigned int aBiggerNum = 4294967295;
    printf("%d %u\n", aNum, aNum);
    printf("%d %u\n", aBiggerNum, aBiggerNum);
    return 0;
}

百分u的必要性

在上面的代码中,整型int变量aNum,数值为2147483647,使用%d%u均能输出正确的结果。

但是无符号整型unsigned int,数值为4294967295,使用%d无法正确输出结果,%u却可以正确输出结果。

下面我们通过它们的二进制表示来分析问题出现的原因,在这之前我们先回忆一个知识点。

整型在计算机中的存储为补码表示法。设一个十进制数为n,模为mod。若n为正数,n的补码为n的二进制。若n为负数,设dec = mod - |n|n的补码为dec的二进制。(具体的文章,可参考:整型数据类型)

1.1 aNum进入printf后的转换情况

我们先观察aNum进入printf函数后的情况。

aNum的值为十进制2147483647,正数的补码为它本身的二进制。因此,aNum的补码为01111111 11111111 11111111 11111111

aNum进入printf后的转换情况

在aNum进入printf函数后,由于aNum的类型为int,所以类型并不会发生转换,仍然为4个字节。

当我们使用%u来做转换规范时,printf会获取4字节的数据,并认为其为无符号整数。它没有符号位,因此肯定为一个正数的补码。可以直接将它转换为十进制,转换结果为字符串"2147483647"。

当我们使用%d来做转换规范时,printf会获取4字节的数据,并认为其为有符号整数。最高位看做符号位,其余为数据位。而在这个二进制中最高位为0,因此它是一个正数的补码,可以直接将它转换为十进制,转换结果为字符串"2147483647"。

在这种情况下,使用%u%d均可以得到正确的转换结果。

由于,以%开始的一串字符指示printf函数如何转换二进制数据,所以将其称为转换规则更为准确。