C语言二进制、八进制、十进制、十六进制深入探究

本文详细介绍了C语言中二进制、八进制和十六进制的表示方法及输出方式,包括不同进制之间的转换和应用示例。

首先来一篇好文:C语言中的二进制数、八进制数和十六进制数

C语言中的整数除了可以使用十进制,还可以使用二进制、八进制和十六进制。

二进制数、八进制数和十六进制数的表示

一个数字默认就是十进制的,表示一个十进制数字不需要任何特殊的格式。但是,表示一个二进制、八进制或者十六进制数字就不一样了,为了和十进制数字区分开来,必须采用某种特殊的写法,具体来说,就是在数字前面加上特定的字符,也就是加前缀。

1) 二进制

二进制由 0 和 1 两个数字组成,使用时必须以0b0B(不区分大小写)开头,例如:

//合法的二进制int a = 0b101;  //换算成十进制为 5
int b = -0b110010;  //换算成十进制为 -50
int c = 0B100001;  //换算成十进制为 33
//非法的二进制
int m = 101010;  //无前缀 0B,相当于十进制
int n = 0B410;  //4不是有效的二进制数字

读者请注意,标准的C语言并不支持上面的二进制写法,只是有些编译器自己进行了扩展,才支持二进制数字。换句话说,并不是所有的编译器都支持二进制数字,只有一部分编译器支持,并且跟编译器的版本有关系。

下面是实际测试的结果:

  • Visual C++ 6.0 不支持。
  • Visual Studio 2015 支持,但是 Visual Studio 2010 不支持;可以认为,高版本的 Visual Studio 支持二进制数字,低版本的 Visual Studio 不支持。
  • GCC 4.8.2 支持,但是 GCC 3.4.5 不支持;可以认为,高版本的 GCC 支持二进制数字,低版本的 GCC 不支持。
  • LLVM/Clang 支持(内嵌于 Mac OS 下的 Xcode 中)。
2) 八进制

八进制由 0~7 八个数字组成,使用时必须以0开头(注意是数字 0,不是字母 o),例如:

//合法的八进制数int a = 015;  //换算成十进制为 13
int b = -0101;  //换算成十进制为 -65
int c = 0177777;  //换算成十进制为 65535
//非法的八进制
int m = 256;  //无前缀 0,相当于十进制
int n = 03A2;  //A不是有效的八进制数字
3) 十六进制

十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,使用时必须以0x0X(不区分大小写)开头,例如:

//合法的十六进制int a = 0X2A;  //换算成十进制为 42
int b = -0XA0;  //换算成十进制为 -160
int c = 0xffff;  //换算成十进制为 65535
//非法的十六进制
int m = 5A;  //没有前缀 0X,是一个无效数字
int n = 0X3H;  //H不是有效的十六进制数字
4) 十进制

十进制由 0~9 十个数字组成,没有任何前缀,和我们平时的书写格式一样,不再赘述。

二进制数、八进制数和十六进制数的输出

C语言中常用的整数有 short、int 和 long 三种类型,通过 printf 函数,可以将它们以八进制、十进制和十六进制的形式输出。上节我们讲解了如何以十进制的形式输出,这节我们重点讲解如何以八进制和十六进制的形式输出,下表列出了不同类型的整数、以不同进制的形式输出时对应的格式控制符:

shortintlong
八进制%ho%o%lo
十进制%hd%d%ld
十六进制%hx 或者 %hX%x 或者 %X%lx 或者 %lX

十六进制数字的表示用到了英文字母,有大小写之分,要在格式控制符中体现出来:

  • %hx、%x 和 %lx 中的x小写,表明以小写字母的形式输出十六进制数;
  • %hX、%X 和 %lX 中的X大写,表明以大写字母的形式输出十六进制数。

八进制数字和十进制数字不区分大小写,所以格式控制符都用小写形式。如果你比较叛逆,想使用大写形式,那么行为是未定义的,请你慎重:

  • 有些编译器支持大写形式,只不过行为和小写形式一样;
  • 有些编译器不支持大写形式,可能会报错,也可能会导致奇怪的输出。

注意,虽然部分编译器支持二进制数字的表示,但是却不能使用 printf 函数输出二进制,这一点比较遗憾。当然,通过转换函数可以将其它进制数字转换成二进制数字,并以字符串的形式存储,然后在 printf 函数中使用%s输出即可。考虑到读者的基础还不够,这里就先不讲这种方法了。

【实例】以不同进制的形式输出整数:

#include <stdio.h>
int main()
{
    short a = 0b1010110;  //二进制数字
    int b = 02713;  //八进制数字
    long c = 0X1DAB83;  //十六进制数字
   
    printf("a=%ho, b=%o, c=%lo\n", a, b, c);  //以八进制形似输出
    printf("a=%hd, b=%d, c=%ld\n", a, b, c);  //以十进制形式输出
    printf("a=%hx, b=%x, c=%lx\n", a, b, c);  //以十六进制形式输出(字母小写)
    printf("a=%hX, b=%X, c=%lX\n", a, b, c);  //以十六进制形式输出(字母大写)
    return 0;
}

运行结果:
a=126, b=2713, c=7325603
a=86, b=1483, c=1944451
a=56, b=5cb, c=1dab83
a=56, b=5CB, c=1DAB83

从这个例子可以发现,一个数字不管以何种进制来表示,都能够以任意进制的形式输出。数字在内存中始终以二进制的形式存储,其它进制的数字在存储前都必须转换为二进制形式;同理,一个数字在输出时要进行逆向的转换,也就是从二进制转换为其他进制。

输出时加上前缀

请读者注意观察上面的例子,会发现有一点不完美,如果只看输出结果:

  • 对于八进制数字,它没法和十进制、十六进制区分,因为八进制、十进制和十六进制都包含 0~7 这几个数字。
  • 对于十进制数字,它没法和十六进制区分,因为十六进制也包含 0~9 这几个数字。如果十进制数字中还不包含 8 和 9,那么也不能和八进制区分了。
  • 对于十六进制数字,如果没有包含 a~f 或者 A~F,那么就无法和十进制区分,如果还不包含 8 和 9,那么也不能和八进制区分了。

区分不同进制数字的一个简单办法就是,在输出时带上特定的前缀。在格式控制符中加上#即可输出前缀,例如 %#x、%#o、%#lX、%#ho 等,请看下面的代码:

#include <stdio.h>
int main()
{
    short a = 0b1010110;  //二进制数字
    int b = 02713;  //八进制数字
    long c = 0X1DAB83;  //十六进制数字
   
    printf("a=%#ho, b=%#o, c=%#lo\n", a, b, c);  //以八进制形似输出
    printf("a=%hd, b=%d, c=%ld\n", a, b, c);  //以十进制形式输出
    printf("a=%#hx, b=%#x, c=%#lx\n", a, b, c);  //以十六进制形式输出(字母小写)
    printf("a=%#hX, b=%#X, c=%#lX\n", a, b, c);  //以十六进制形式输出(字母大写)
   
    return 0;
}

运行结果:
a=0126, b=02713, c=07325603
a=86, b=1483, c=1944451
a=0x56, b=0x5cb, c=0x1dab83
a=0X56, b=0X5CB, c=0X1DAB83

十进制数字没有前缀,所以不用加#。如果你加上了,那么它的行为是未定义的,有的编译器支持十进制加#,只不过输出结果和没有加#一样,有的编译器不支持加#,可能会报错,也可能会导致奇怪的输出;但是,大部分编译器都能正常输出,不至于当成一种错误。



下面来实战一下,验证一下数值在不同进制的表示下的情况

首先我们总结一下上面的内容:

  1. 各进制表示

    • 二进制表示:使用时必须以0b0B(不区分大小写)开头
    • 八进制表示:使用时必须以0开头(注意是数字 0,不是字母 o)
    • 十六进制表示:使用时必须以0x0X(不区分大小写)开头
  2. 各进制打印

    • 二进制打印:C语言没有提供打印数值二进制表示的方式
    • 八进制打印:printf("%o", n) printf("%#o", n)
    • 十进制打印:printf("%d", n) printf("%#d", n)
    • 十六进制打印:printf("%x", n)
  3. 基本常识:不管是哪种进制,在计算机里面都是以0/1来表示的,说的更具体一点,都是以补码的形式存在。

一、首先演示一下用不同进制来表示同一个数值

#include <stdio.h>

//该函数用来打印unsigned char的二进制表示
void printf_bin_8(unsigned char num)
{
    int k;
    unsigned char *p = (unsigned char*)&num;

    for (int k = 7; k >= 0; k--) //处理8个位
    {
        if (*p & (1 << k))
            printf("1");
        else
            printf("0");
    }
    printf("\r\n");
}

int main() {
	unsigned char a = 99;
	printf("二进制表示为:0b"); 
	printf_bin_8(a);
	printf("八进制表示为:%#o\n",a);
	printf("十进制表示为:%d\n",a);
	printf("十六进制表示为:%#x\n",a);
}

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210716234600063.png#pic_center
https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210716234659589.png

二、不同进制的数据可以比较大小和进行数值运算吗?

int main() {
	int a = 99;//十进制99
	int b = 0b01100011;//二进制99
	int c = 0143;//八进制99
	int d = 0x63;//十六进制99
	
	if(a == b && b == c && c == d)
	    printf("不同进制表示的数据是可以比较的");
	
	printf("\na+b=%d",a + b);
	printf("\na-b=%d",a - b);
	printf("\na*b=%d",a * b);
	printf("\na/b=%d",a / b);
}

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210716235717631.png
可以看出不同进制表示的数据可以进行大小比较和运算的,其实只要我们理解了,不管什么进制表示的数据在计算机中都是以二进制补码的方式存储的。

在进行大小比较或者运算时,我们可以都将其转成同一个进制的表示就可以了。

三、八进制、十六进制的数值表示可以作为数组索引吗?

int main() {
	int a = 99;
	int b = 0b01100011;
	int c = 0143;
	int d = 0x63;
	
	int arr[100];
	arr[99] = 55;
	printf("%d",arr[a]);
	printf("\n%d",arr[b]);
	printf("\n%d",arr[c]);
	printf("\n%d",arr[d]);
	printf("\n%d",arr[0b01100011]);
	printf("\n%d",arr[0143]);
	printf("\n%d",arr[0x63]);
}

在这里插入图片描述

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机载软件与适航

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值