问题引出

C语言中,在需要用到16进制数据的时候,可以通过printf函数的%x格式打印数据的16进制形式。在某些位标记、位操作的场合,需要用到2进制格式的数据,但printf函数不能输出2进制格式,虽然可以通过使用itoa_itoa的方法转为2进制的字符串打印,但显示的长度是不固定的,无法显示有效数位前面的0。

例如:现在需要打印数字258的2进制格式,且需要将32位全部显示出来,即想要得到结果00000000 00000000 00000001 00000010,而使用_itoa的方法和打印结果为:

int a = 258;
char s[32];
_itoa(a, s, 2);
printf("a --> %sn", s);
a --> 100000010

不是我们想要的格式!

数据存储原理探析

那要怎么办呢?自己写个小程序吧,思路如下:

首先弄清楚数据在计算机中是如何存储的,对应int型数字,在32或64位计算机中都占4个字节,而计算机中的数据存储是以字节(Byte)为单位,1个字节包含8个位(bit),例如,数字258的16进制形式为0x00000102,2进制形式为:00000000 00000000 00000001 00000010,其在计算机内存中的存储方式如图所示:

右侧的16进制数是内存的地址,向上递增,方框里的二进制数是内存单元实际存储的字节内存,我们可以通过程序测试验证一下,因为unsigned char或char类型在系统是占用一个字节,因此可以定义该变量的指针,分别指向int的4个字节,打印其内存地址和实际存储的内存进行验证,代码如下:

int a = 258;
//使用unsigned char来验证int的每一个字节
unsigned char *p1 = (unsigned char*)&a;   //获取a的首地址
unsigned char *p2 = (unsigned char*)&a+1; //获取a的首地址的后一个字节地址
unsigned char *p3 = (unsigned char*)&a+2; //获取a的首地址的后两个字节地址
unsigned char *p4 = (unsigned char*)&a+3; //获取a的首地址的后三个字节地址
printf("[a] p1:%x, %drn", p1, *p1); //打印p1的地址与存储的字节内容
printf("[a] p2:%x, %drn", p2, *p2); //打印p2的地址与存储的字节内容
printf("[a] p3:%x, %drn", p3, *p3); //打印p3的地址与存储的字节内容
printf("[a] p4:%x, %drn", p4, *p4); //打印p4的地址与存储的字节内容

运行结果:

[a] p1:5216f804, 2
[a] p2:5216f805, 1
[a] p3:5216f806, 0
[a] p4:5216f807, 0

可以看出,随着地址的增加,存储的内存依次是2、1、0、0,对应数字258的从底到高的4个字节的值,另外每次运行程序,变量a的地址是自动分配的,所以每次的输出与上面示意图的地址是不同的,但都是4个连续增加的地址值。

另外补充一下,这是一种小端字节序的存储方法,即将一个数据的低字节存储在内存的低地址,或理解为先存储数据的低字节。与之对应的是大端字节序存储方式,即先存储数据的高位字节,类似与我们书写数字时从左到右先写高位数字一样。由于计算机先处理地位字节的效率比较高,因此计算机内部的数据处理均采用小端字节序,而为了方便人的阅读,除了计算机内部处理,其它场合是采用大端字节序的。可通过下面的示意图助记:

另外,需要区分一点,无论大端还小端字节序,在一个字节的内部的8位2进制数,都是按照人类的习惯从左到右存放,如00000010

在字节中也是按照这样存储的,不需要反过来。

C代码实现

分析了这么多,可以编写代码来输出一个数字的2进制格式形式了。具体的代码实现:

void printf_bin(int num)
{int i, j, k;unsigned char *p = (unsigned char*)&num + 3;//p先指向num后面第3个字节的地址,即num的最高位字节地址for (i = 0; i < 4; i++) //依次处理4个字节(32位){j = *(p - i); //取每个字节的首地址,从高位字节到低位字节,即p p-1 p-2 p-3地址处for (int k = 7; k >= 0; k--) //处理每个字节的8个位,注意字节内部的二进制数是按照人的习惯存储!{if (j & (1 << k))//1左移k位,与单前的字节内容j进行或运算,如k=7时,00000000&10000000=0 ->该字节的最高位为0printf("1");elseprintf("0");}printf(" ");//每8位加个空格,方便查看}printf("rn");
}

数字258的实际运行效果:

00000000 00000000 00000001 00000010

对应unsigned char型的数字,如果只需要显示8位2进制数,可以对上述小程序简化:

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");elseprintf("0");}printf("rn");
}

数字12的运行效果

00001100

关于负数

上面测试的都是正数,当然,对于负数,也是可以显示的,因为负数在计算机中是以对应正数的补码来存储的,因而显示的2进制数也是补码形式,这里顺便再复习一下补码:

对于负数-9,对应的正数位9,9的原码,反码,补码如下:

这里只画出8位示意,int型数字实际是占32位,对应的高位全是0或1。

使用程序测试一下-9的2进制格式输出:

11111111 11111111 11111111 11110111

可以正确显示。

完整测试代码

下面是整个本文的整个测试程序:

#include <stdio.h>
#include <stdlib.h>void printf_bin(int num)
{int i, j, k;unsigned char *p = (unsigned char*)&num + 3;for (i = 0; i < 4; i++) //处理4个字节(32位){j = *(p - i); //取每个字节的首地址for (int k = 7; k >= 0; k--) //处理每个字节的8个位{if (j & (1 << k))printf("1");elseprintf("0");}printf(" ");}printf("rn");
}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");elseprintf("0");}printf("rn");
}int main()
{int a = 258;int b = -9;printf("定义int a=%d, int b=%drn", a, b);//使用unsigned char来验证int的每一个字节unsigned char *p1 = (unsigned char*)&a;  //获取a的首地址unsigned char *p2 = (unsigned char*)&a+1;//获取a的首地址的后一个字节地址unsigned char *p3 = (unsigned char*)&a+2;//获取a的首地址的后两个字节地址unsigned char *p4 = (unsigned char*)&a+3;//获取a的首地址的后三个字节地址printf("rn查看a的每个字节的地址与内容:rn");printf("[a] p1:%x, %drn", p1, *p1);//打印p1的地址与存储的字节内容printf("[a] p2:%x, %drn", p2, *p2);//打印p2的地址与存储的字节内容printf("[a] p3:%x, %drn", p3, *p3);//打印p3的地址与存储的字节内容printf("[a] p4:%x, %drn", p4, *p4);//打印p4的地址与存储的字节内容p1 = (unsigned char*)&b;p2 = (unsigned char*)&b + 1;p3 = (unsigned char*)&b + 2;p4 = (unsigned char*)&b + 3;printf("rn查看b的每个字节的地址与内容:rn");printf("[b] p1:%x, %drn", p1, *p1);printf("[b] p2:%x, %drn", p2, *p2);printf("[b] p3:%x, %drn", p3, *p3);printf("[b] p4:%x, %drn", p4, *p4);//自己的方法1printf("rna的2进制格式(显示32位):rn");printf_bin(a);printf("rnb的2进制格式(显示32位):rn");printf_bin(b);//自己的方法2unsigned char c = 12;printf("rn定义unsigned char c=%drn", c);printf("c的2进制格式(显示8位):rn");printf_bin_8(c);//调用函数的方法printf("rn使用_itoa函数显示2进制格式rn");char s[32];_itoa(a, s, 2);printf("a --> %sn", s);_itoa(b, s, 2);printf("b --> %sn", s);   _itoa(c, s, 2);printf("c --> %sn", s);getchar();return 0;
}

运行结果:

定义int a=258, int b=-9查看a的每个字节的地址与内容:
[a] p1:a739f4d4, 2
[a] p2:a739f4d5, 1
[a] p3:a739f4d6, 0
[a] p4:a739f4d7, 0查看b的每个字节的地址与内容:
[b] p1:a739f4f4, 247
[b] p2:a739f4f5, 255
[b] p3:a739f4f6, 255
[b] p4:a739f4f7, 255a的2进制格式(显示32位):
00000000 00000000 00000001 00000010b的2进制格式(显示32位):
11111111 11111111 11111111 11110111定义unsigned char c=12
c的2进制格式(显示8位):
00001100使用_itoa函数显示2进制格式
a --> 100000010
b --> 11111111111111111111111111110111
c --> 1100

c语言输出方框□怎么回事_C语言打印数据的二进制格式-原理解析与编程实现相关推荐

  1. c语言输出方框□怎么回事_值得收藏的 C语言指针讲解文章,确实不错!

    来源 :https://zlc.im/language/c/c_and_pointer.md#%E6%8C%87%E9%92%88 指针 本文介绍C语言的指针相关知识. 指针是什么? 指针和其他的in ...

  2. c语言输出菱形for循环_c语言for循环如何打印菱形

    c语言for循环打印菱形的方法:使用两个for循环,实现条件判断,代码为[int i,j;for(i=0; i<2*n-1; i++){if(i=n-i-1&&j c语言for循 ...

  3. c语言输出菱形for循环_c语言输出菱形

    07 以下是所有的代码: #include void print(int n) { //判断n是否符合正奇数的要求 if(n < 0 || n % 2 == 0) //不符合则直接退出 retu ...

  4. c语言prime函数怎么用_C语言教程_v20201106

    编程基础 1.1    通俗地理解什么是编程语言 1.2    C语言究竟是一门怎样的语言? 1.6    初中毕业能学会编程吗? 1.7    C和C++到底有什么关系? 1.8    学了 C 语 ...

  5. vc 运行c语言步骤,第1章_C语言概述(vc++环境如何运行c语言程序)[精选].ppt

    第1章_C语言概述(vc环境如何运行c语言程序)[精选] C语言程序设计 授课对象:电子商务06 时间安排: 理论课(1-9周): 星期一 5-6节 9407D 星期三 5-6节 9410D 实验课( ...

  6. c程序设计语言 qsort,【程序设计基础_C语言】北理工的恶龙(附qsort范例)

    [程序设计基础_C语言]北理工的恶龙(附qsort实例) 北理工的恶龙(附qsort实例) 背景:最近,北理工出现了一只恶龙,它长着很多 头,而且还会吐火,它将会把北理工烧成废墟, 于是,校长下令召集 ...

  7. r语言将百分数化为小数_C语言入门学习(一)

    知识点1[写代码的过程] 编辑器:程序员写代码的过程(记事本.vc6.0.vim)(让程序员看懂) 编译器:查看代码的语法错误,生成汇编语言. 汇编器:将生成好汇编语言 生成 二进制语言(目标文件) ...

  8. c语言最小点对问题_C语言教程(一):程序开发理论基础!

    前言: 1.本系列教程适用于刚开始学习 C 语言的同学,或者初级开发者,或者想系统详细了解 C 语言的小伙伴. 2.本系列教程会从C语言基础到 C 语言高级语法,包含 C 语言所有系列. C语言简介 ...

  9. c语言n次方怎么输入_C语言基本数据类型的思维导图

    在使用C语言编写程序时,数据类型是一个非常重要的内容,任何一个不被重视的数据错误都会使编译器无法翻译,导致程序报错. 使用思维导图来梳理各个数据类型是一个很有效的记忆方法,接下来就为大家展示一下我用i ...

最新文章

  1. java冒泡排序_Java冒泡排序,鸡尾酒排序
  2. hdu 4506(快速幂+找规律)
  3. Sentry UDFWhiteList bug分析
  4. pdo sqlite_ sqlite2 pdo_mysql_php使用pdo连接sqlite3的配置示例
  5. linux之解决在ubuntu上面打开window文件乱码问题总结
  6. mybatisplus 操作另一个数据库的数据_MySQL数据库的基础操作
  7. [转]每秒处理10万订单乐视集团支付架构
  8. zookeeper 3.5.6安装
  9. 神泣单机服务器维护,神泣9.0网游单机版网单一键服务端 魔王归来
  10. 佳易王收银管理软件和扫码通M800大部分扫码支付平台对接步骤:
  11. 软件测试每日例会,每日例会(二).md
  12. 基于python的异方差检验_异方差检验及解决方法
  13. 最新的鄞比 服务器mp4,阳光之下播出,彭冠英好适合傅慎行,和李承鄞比肩的人出来了...
  14. ACM在线测试题C++实现蛇形填数
  15. DL4J中文文档/开始/从源码构建
  16. Python【王者荣耀】全英雄无水印皮肤
  17. 六种找客户的方法销售人员需掌握
  18. 前端实现“查看更多”效果
  19. 坯子库和suapp哪个好用_关于Sketchup插件安装使用,你应该知道的那些事
  20. OpenStack权威技术认证COA简介及全球实考统计数据

热门文章

  1. [unity]实现切水果游戏
  2. springMVC 面试题整理
  3. Java for函数用法_Java函数式编程(四)集合的使用
  4. webstrom免安装版
  5. 百度云曲显平:AIOps时代下如何用运维数据系统性地解决运维问题?
  6. 网络基础——100道面试题,你能答对多少?
  7. WPS Office 2019文字组件插入和改写状态的切换
  8. 基于 Spring Boot 的停车场管理系统
  9. 零基础入门网络安全最直线距离的学习路线
  10. 你的孤独,正在撑起一个万亿级市场