**问题引出**
C言語では、16進データが必要な場合、16進形式のデータをprintf関数の%x形式で出力できます。一部のビットマーキングやビット操作の場合、バイナリ形式のデータが必要ですが、printf関数はitoaまたは_itoaを使用してバイナリ文字列印刷に変換できますが、バイナリ形式を出力できません。ただし、表示の長さは固定されておらず、有効な桁の前の0は表示できません。
例:ここで、数値258のバイナリ形式を印刷する必要があり、32ビットすべてを表示する必要があります。つまり、結果00000000 00000000 00000001 00000010、および_itoaの使用方法と印刷結果を取得する必要があります。は:
int a = 258;
char s[32];
_itoa(a, s, 2);
printf("a --> %s\n", s);
a --> 100000010
私たちが望む32ビットフォーマットではありません!
** 数据存储原理探析**
私は何をすべきか?自分で小さなプログラムを書いてください。アイデアは次のとおりです。
まず、データがコンピューターにどのように格納されているかを理解します。int番号の場合、32ビットまたは64ビットのコンピューターでは4バイトを占め、コンピューターのデータストレージはバイト(バイト)を単位として計算されます。 1ワードこのセクションには8ビットが含まれています。たとえば、番号258の16進形式は0x00000102、2進形式は00000000 00000000 00000001 00000010であり、コンピュータメモリへの保存方法を次の図に示します。
右側の16進数はメモリのアドレスで、上に向かって増加します。ボックス内の2進数は、メモリユニットに実際に格納されているバイトメモリです。符号なしcharまたはcharタイプが1つを占めるため、プログラムテストで確認できます。システムバイトでは、変数のポインタを定義し、それぞれ4バイトのintをポイントし、検証のためにそのメモリアドレスと実際のメモリを出力できるため、コードは次のようになります。
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, %d\r\n", p1, *p1); //打印p1的地址与存储的字节内容
printf("[a] p2:%x, %d\r\n", p2, *p2); //打印p2的地址与存储的字节内容
printf("[a] p3:%x, %d\r\n", p3, *p3); //打印p3的地址与存储的字节内容
printf("[a] p4:%x, %d\r\n", 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つの連続して増加するアドレス値です。
また、これはリトルエンディアンの記憶方式です。つまり、データの下位バイトがメモリの下位アドレスに格納されるか、最初にデータの下位バイトとして理解されます。これに対応するのがビッグエンディアンの記憶方法です。つまり、数値を書き込むときに左から右に上位の数値を書き込むのと同じように、データの上位バイトが最初に格納されます。コンピュータはステータスバイトを最初に処理する方が効率的であるため、コンピュータの内部データ処理はリトルエンディアンのバイト順序を採用し、コンピュータの内部処理に加えて、人間が読むのに便利なように、ビッグエンディアンを使用する場合もあります。バイトオーダー。次の回路図ニーモニックを使用できます。
さらに、1つのポイントを区別する必要があります。ビッグエンディアンまたはリトルエンディアンのバイト順序に関係なく、バイト内の8桁の2進数は、バイト単位の00000010など、人間の習慣に従って左から右に格納されます。逆にする必要はありません。
** C代码实现**
たくさん分析した後、バイナリ形式で数値を出力するコードを書くことができます。特定のコードの実装:
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 ->该字节的最高位为0
printf("1");
else
printf("0");
}
printf(" ");//每8位加个空格,方便查看
}
printf("\r\n");
}
番号258の実際の操作効果:
00000000 00000000 00000001 00000010
unsigned char型の数に対応して、8桁の2進数のみを表示する必要がある場合は、上記の小さなプログラムを簡略化できます。
void printf_bin_8(unsigned char num)
{
int k;
unsigned char *p = (unsigned char*)#
for (int k = 7; k >= 0; k--) //处理8个位
{
if (*p & (1 << k))
printf("1");
else
printf("0");
}
printf("\r\n");
}
番号12の実行結果:
00001100
** 关于负数**
上記のテストはすべて正の数です。もちろん、負の数は正の数の補数としてコンピューターに保存されるため、負の数も表示できます。表示される2進数も補数の形式になります。で確認しましょう。方法。1つの補足:
負の-9の場合、対応する正の桁は9であり、元のコード、逆数コード、および補数コードは次のとおりです。
ここでは8ビットのみが描画され、int型の数値は実際には32ビットを占め、対応する上位ビットはすべて0または1です。
プログラムを使用して、バイナリ形式で-9の出力をテストします。
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");
else
printf("0");
}
printf(" ");
}
printf("\r\n");
}
void printf_bin_8(unsigned char num)
{
int k;
unsigned char *p = (unsigned char*)#
for (int k = 7; k >= 0; k--) //处理8个位
{
if (*p & (1 << k))
printf("1");
else
printf("0");
}
printf("\r\n");
}
int main()
{
int a = 258;
int b = -9;
printf("定义int a=%d, int b=%d\r\n", 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("\r\n查看a的每个字节的地址与内容:\r\n");
printf("[a] p1:%x, %d\r\n", p1, *p1);//打印p1的地址与存储的字节内容
printf("[a] p2:%x, %d\r\n", p2, *p2);//打印p2的地址与存储的字节内容
printf("[a] p3:%x, %d\r\n", p3, *p3);//打印p3的地址与存储的字节内容
printf("[a] p4:%x, %d\r\n", p4, *p4);//打印p4的地址与存储的字节内容
p1 = (unsigned char*)&b;
p2 = (unsigned char*)&b + 1;
p3 = (unsigned char*)&b + 2;
p4 = (unsigned char*)&b + 3;
printf("\r\n查看b的每个字节的地址与内容:\r\n");
printf("[b] p1:%x, %d\r\n", p1, *p1);
printf("[b] p2:%x, %d\r\n", p2, *p2);
printf("[b] p3:%x, %d\r\n", p3, *p3);
printf("[b] p4:%x, %d\r\n", p4, *p4);
//自己的方法1
printf("\r\na的2进制格式(显示32位):\r\n");
printf_bin(a);
printf("\r\nb的2进制格式(显示32位):\r\n");
printf_bin(b);
//自己的方法2
unsigned char c = 12;
printf("\r\n定义unsigned char c=%d\r\n", c);
printf("c的2进制格式(显示8位):\r\n");
printf_bin_8(c);
//调用函数的方法
printf("\r\n使用_itoa函数显示2进制格式\r\n");
char s[32];
_itoa(a, s, 2);
printf("a --> %s\n", s);
_itoa(b, s, 2);
printf("b --> %s\n", s);
_itoa(c, s, 2);
printf("c --> %s\n", 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, 255
a的2进制格式(显示32位):
00000000 00000000 00000001 00000010
b的2进制格式(显示32位):
11111111 11111111 11111111 11110111
定义unsigned char c=12
c的2进制格式(显示8位):
00001100
使用_itoa函数显示2进制格式
a --> 100000010
b --> 11111111111111111111111111110111
c --> 1100