一 clang 分四步编译main.c
这里用的clang/clang++ 分四步编译main.c/main.cpp文件
1.1 C++源文件
#include <iostream>
int main() {
std::cout << "Hello Biter !" << std::endl;
return 0;
}
-c600
-c600
-c600
1.2 预处理、编译、汇编、链接
clang或者clang++方式
<div STYLE="page-break-after: always;"></div>
1.2.1 预处理
命令:
-c400clang++ -E main.cpp -o main.ii
<div STYLE="page-break-after: always;"></div>
1.2.2 编译阶段,生成汇编
clang++ -S main.ii -o main.s
-c400
<div STYLE="page-break-after: always;"></div>
1.2.3 汇编阶段,生成目标文件
clang++ -c main.s -o mian.o
-c400
<div STYLE="page-break-after: always;"></div>
1.2.4 连接阶段
clang++ mian.o -o main
-c400
<div STYLE="page-break-after: always;"></div>
1.2.5 执行
./main
-c400
<div STYLE="page-break-after: always;"></div>
1.3 四部曲之一步到胃
clang++在编译的过程中,保存所有编译过程中产生的文件
1.3.1 产生中间文件
clang++ -save-temps main.cpp -o main
-c400
1.3.2 不保存中间文件
clang++ main.cpp -o main
<div STYLE="page-break-after: always;"></div>
二 持有ndk的交叉编译工具 编译main.c
2.1 预处理
armv7a-linux-androideabi29-clang++ -E main.cpp -o main.ii
-c400
<div STYLE="page-break-after: always;"></div>
2.2 编译
armv7a-linux-androideabi29-clang++ -S main.ii -o main.s
-c400
<div STYLE="page-break-after: always;"></div>
2.3 汇编
armv7a-linux-androideabi29-clang++ -c main.s -o mian.o
-c400
<div STYLE="page-break-after: always;"></div>
2.4 连接
armv7a-linux-androideabi29-clang++ mian.o -o main
-c400
<div STYLE="page-break-after: always;"></div>
3 原码、反码、补码
3.1 知识点补充
在计算机内部,所有信息都是用二进制数串的形式表示的。整数通常都有正负之分,计算机中的整数分为无符号的和带符号的。无符号的整数用来表示0和正整数,即自然数;带符号的正数可以表示所有的整数。
由于计算机中符号和数字一样,都必须用二进制数串来表示,因此,正负号也必须用0、1来表示。通常我们用最高的有效位来 表示数的符号(当用8位来表示一个整数时,第8位即为最高有效位,当用16位来表示一个整数时,第16位即为最高有效位。)0表示正号、1表示负号。
这种正负号数字化(0表示正号、1表示负号)的机内表示形式就称为机器码或者机器数,而相应的机器外部用正负号表示的数称为真值。将一个真值表示成二进制字串的机器数的过程就称为编码
。
无符号数没有原码、反码和补码一说。只有带符号数才存在不同的编码方式。带符号整数有原码、反码、补码等几种编码方式。
原码即直接将真值转换为其相应的二进制形式,而反码和补码是对原码进行某种转换编码方式。
正整数
的原码
、反码
和补码
都一样。
负数的反码是对原码的除符号位外的其他位进行取反后的结果(取反即如果该位为0则变为1,而该位为1则变为0的操作)。而补码是先求原码的反码,然后在反码的末尾位加1 后得到的结果,即补码是反码+1。
IBM-PC中带符号整数都采用补码形式表示。
<font color="brown">注意,只是带符号的整数采用补码存储表示的,浮点数另有其存储方式。</font>
- 正数的补码是其本身
- 负数的反码,符号位不变,其余的按位取反
- 负数的补码,反码加1
对于字长为8位有符号的int,因为最高为符号位,占1位,所以最小为(11111111)2 = (-127)10,最大为(01111111)2 = (127)10;即其原码范围为:-127~127
有符号的8位二进制的原值表达范围为:-127至127,此时共255个数字;然而,8位二进制 的补码排列共有 = 256个,0000 0000 至1111 1111 。
机器码组合 | 范围 | 个数 |
---|---|---|
0000 0000 - 0111 1111 | 0 ~+127 | 128 |
<font size="3" color="brown">10000000 </font> | 多余的一种组合待定
|
1 |
1000 0001 - 1111 1111 | -1~-127 | 127 |
<font size="3" color="brown">10000000 </font>看似要被浪费掉了啊!其实不然,( 100000000 ) 2 = ( 2^7 ) 10 = ( 128 ) 10,这个组合要利用起来,不能太偏离数值意义,表示128,显得更直观。
<div STYLE="page-break-after: always;"></div>
从大到小,依次减1看一下规律:
十进制 (字长8bit) | 原码 | 反码 | 补码 |
---|---|---|---|
127 | 0111 1111 | 0111 1111 | 0111 1111 |
126 | 0111 1110 | 0111 1110 | 0111 1110 |
…… | …… | …… | …… |
10 | 0000 1010 | 0000 1010 | 0000 1010 |
…… | …… | …… | …… |
2 | 0000 0010 | 0000 0010 | 0000 0010 |
1 | 0000 0001 | 0000 0001 | 0000 0001 |
+0 | 0000 0000 | 0000 0000 | 0000 0000 |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
-2 | 1000 0010 | 1111 1101 | 1111 1110 |
…… | …… | …… | …… |
-10 | 10001010 | 11110101 | 11110110 |
…… | …… | …… | …… |
-127 | 11111111 | 10000000 | 10000001 |
待定 | <font size="3" color="brown">10000000 </font> |
从递减规律中,发现,<font size="3" color="brown">10000000 </font>表示-128
更合适。
即规定:-128的补码为 <font size="3" color="brown">10000000 </font>
求10
-10
0
-128
127
的原码、反码、补码
十进制 (字长8bit) | 原码 | 反码 | 补码 |
---|---|---|---|
10 | 00001010 | 00001010 | 00001010 |
-10 | 10001010 | 11110101 | 11110110 |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
+0 | 00000000 | 00000000 | 00000000 |
-0 | |||
-128 | 无 | 无 | <font size="3" color="brown">10000000 </font> |
127 | 01111111 | 01111111 | 01111111 |
-127 | 11111111 | 10000000 | 10000001 |
+0和-0的补码是一样的。即 0的补码只有一种表示,0的补码是0000 0000,
<div STYLE="page-break-after: always;"></div>
四 输出结果,解释为什么是这样的
char c = 128;
printf("%d\n",c);
printf("%hhd\n",c);
printf("%hd\n",c);
printf("%hu\n",c);
4.1 格式输出符
格式符号 | 意义 |
---|---|
%a | 浮点数、十六进制数字和p-记数法 (C99) |
%A | 浮点数、十六进制数字和P-记数法 (C99) |
%c | 一个字符 |
%d | 有符号十进制整数 |
%e | 浮点数、e-记数法 |
%E | 浮点数、E-记数法 |
%f | 浮点数,十进制记数法 |
%g | 根据数值不同自动选择%f或者%e。%e格式在指数小于-4或者大于等于精度时使用 |
%G | 根据数值不同自动选择%f或者%E。%E格式在指数小于-4或者大于等于精度时使用 |
%i | 有符号十进制整数 (与%d相同) |
%o | 无符号八进制整数 |
%p | 指针(就是指地址) |
%s | 字符串 |
%u | 无符号十进制整数 |
%x | 使用十六进制数字0f 的无符号十六进制整数 |
%X | 使用十六进制数字0F的无符号十六进制整数 |
%% | 打印一个百分号 |
<div STYLE="page-break-after: always;"></div>
4.2 格式输出其修饰符
修饰符 | 意义 | 示例 |
---|---|---|
h | 和整数转换说明符一起使用,表示一个short int 或者 unsigned short int 类型数值。 | "%hd |
hh | 和整数转换说明符一起使用,表示一个signed char 或者unsigned char类型数值。 | "%hhd" |
j | 和整数转换说明符一起使用,表示一个intmax_t或uintmax_t值。 | "%jd" |
l | 和整数说明符一起使用,表示一个long int 或者unsigned long int 类型值。 | "%8lu" |
ll | 和整数说明符一起使用,表示一个long long int或 unsigned long long int 类型值 (C99)。 | "%lld" |
L | 和浮点转换说明符一起使用,表示一个long double值。 | "%8.4Le" |
t | 和整数转换说明符一起使用,表示一个ptrdiff_t值(与两个指针之间的差相对应的类型) (C99) | "%td" |
- | 项目是左对齐的,也就是说,会把项目打印在字段的左侧开始处 | "%-20s" |
+ | 有符号的值若为正,则显示带加号的符号;若为负,则带减号的符号。 | "%+3.2" |
(空格) | 有符号的值若为正,则显示时带前导空格(但是不显示符号);若为负,则带减号符号。+标志会覆盖空格标志。 | "% 3.2" |
# | 使用转换说明的可选形式。若为%o格式,则以0开始;若为%x和%X格式,则以0x或0X开始,对于所有的浮点形式,#保证了即使不限任何数字,也打印一个小数点字符。对于%g和%G格式,它防止尾随零被删除。 | "%#o" |
0 | 对于所有的数字格式,用前导零而不是用空格填充字段宽度。如果出现-标志或者指定了精度(对于整数)则忽略该标志。 | "%010d" |
<div STYLE="page-break-after: always;"></div>
4.3 机器码求解
char c = 128;
此处是将一个int赋值给一个char类型变量,进行隐式类型转换.int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。
首先,整型128在一个字长为4个字节的的原码为00000000 00000000 00000000 10000000
,当把一个int类型赋值给一个有符号的char类型时,高位被舍弃。实际给c的是10000000
,此时,被系统认为是一个负数,补码为10000000
,结合上面的分析,其值就是-128。
4.4 格式化输出
char c = 128;
4.4.1 结论
<font color="blue">先给出通过这次作业得出的一个不完全归纳法结论吧,也是我做出的解释。最后会给出原码验证</font>
1、<font color="brown">正数的原码的反码、补码是其本身,扩展时,高位补0;</font>
2、<font color="grey">负数扩展为有符号的高位补1,无符号的高位补0。</font>
2、<font color="brown" size=3px >负数扩展时,高位补 1。格式化输出无符号的数据时,机器码即为原码;格式化输出有符号数据时,要先求其原码,然后求得真值。</font>
c 的机器码为10000000 。
4.4.2 输出32位有符号int:
printf("%d\n",c);//默认的 int ,32位
将1000 0000 转为有符号的32位机器码:1111 1111 1111 1111 1111 1111 1000 0000
反码:1111 1111 1111 1111 1111 1111 0111 1111
原码:1000 0000 0000 0000 0000 0000 1000 0000
<font color="brown">有符号,其值为:-128.</font>
4.4.3 输出8位有符号signed char:
printf("%hhd\n",c);//signed char, 8位
8位机器码:1000 0000,
<font color="brown">此机器码没有反码和源码,机器码即为真值:-128.</font>
4.4.4 输出16位有符号 short int
printf("%hd\n",c);//short int,16 位
将1000 0000 转为有符号的16位机器码:1111 1111 1000 0000
反码:1111 1111 0111 1111 原码:1000 0000 1000 0000
<font color="brown">有符号,其值为:-128.</font>
4.4.5 输出16位无符号 short int
printf("%hu\n",c);
将1000 0000 转为16位机器码:1111 1111 1000 0000
格式化输出无符号十进制数据,此码即为原码。
原码:1111 1111 1000 0000
<font color="brown">做无符号运算,其值:65408</font>
4.5 源码
#include <iostream>
#include <bitset>
int main() {
using namespace std;
cout << "Hello Biter !" << endl;
char c = 128;
cout << "---------------- -" << int(c) << "----------------------- " << endl;
cout << "char 型 机器码 = " << bitset<sizeof(char) * 8>(c) << endl;
cout << "int 型 机器码 = " << bitset<sizeof(int) * 8>(c) << endl;
cout << "signed char 型 机器码 = " << bitset<sizeof(signed char) * 8>(c) << endl;
cout << "short int 型 机器码 = " << bitset<sizeof(short int) * 8>(c) << endl;
cout << "unsigned short int 型 机器码 = " << bitset<sizeof(unsigned short int) * 8>(c) << endl;
cout << "char 二进制形式为 = " << bitset<sizeof(char) * 8>(c) << endl;
cout << "-------------------------------------------- " << endl;
// printf("将 char 直接 输出 128 超范围了 = %c\n", c);
// cout << "-------------------------------------------- " << endl;
printf("有符号的 int 输出 = %d\n", c);
cout << "-------------------------------------------- " << endl;
printf("有符号的 signed char 输出 = %hhd\n", c);//1000,0000
cout << "-------------------------------------------- " << endl;
printf("有符号的 short int 输出= %hd\n", c);//0000,0000 1000,0000
cout << "-------------------------------------------- " << endl;
printf("无符号的 unsigned short int 输出= %hu\n", c);// 1111,1111 1000,0000
cout << "-------------------------------------------- " << endl;
return 0;
}
网友评论