08 C各种进制、存储细节、位运算
2018-10-30 本文已影响0人
亨锅锅
1、基本理解
#include <stdio.h>
int main()
{
/*
*
八进制 十六进制 错 十进制 错 十进制 十进制
00011 0x001 0x7h4 10.98 0986 .089 -109
十进制 错 二进制 十六进制 错 十进制 错 十进制
+178 0b325 0b0010 0xffdc 96f 96.0f 96.oF -.003
在C语言中, 如果想用十六进制表示某个数, 前面需要加上0x
在C语言中, 如果想用八进制表示某个数, 前面需要加上0
在C语言中, 如果想用二进制表示某个数, 前面需要加上0b
*/
// 注意点:
// 1.%i %d代表以十进制输出整数
// 2.0x和0X一样,0b和0B一样
// // 用十进制来表示
// int num1 = 12;
// printf("num1 = %i\n", num1);
// // 用十六进制来表示
// int num2 = 0xc;
// printf("num2 = %i\n", num2);
// // 用八进制来表示
// int num3 = 014;
// printf("num3 = %i\n", num3);
// // 用二进制来表示
// int num4 = 0b1100;
// printf("num4 = %i\n", num4);
int num = 12;
// 以十进制输出
printf("%i\n", num);
// 以八进制输出
printf("%o\n", num);
// 以十六进制输出
printf("%x\n", num);
// 以二进制输出必须自己实现(后续会讲解如何实现)
return 0;
}
2、进制转换
#include <stdio.h>
int main()
{
/*
* 进制转换
* 十进制 --> 二进制
* 二进制 --> 八进制
* 二进制 --> 十六进制
*/
/*
* 十进制 --> 二进制
* 规则: 除2取余, 余数倒叙
*
*
* 12
* 2
* ---
* 6 0
* 2
* ---
* 3 0
* 2
* ---
* 1 1
* 2
* ---
* 0 1
*
* 12 --> 1100
*/
/*
* 15
* 2
* ---
* 7 1
* 2
* ---
* 3 1
* 2
* ---
* 1 1
* 2
* ---
* 0 1
*/
int num = 0b1111;
printf("%i", num);
return 0;
}
#include <stdio.h>
int main()
{
/*
* 进制转换
* 十进制 --> 二进制
* 二进制 --> 十进制
* 二进制 --> 八进制
* 二进制 --> 十六进制
*/
/*
* 十进制 --> 二进制
* 规则: 除2取余, 余数倒叙
*
*
* 12
* 2
* ---
* 6 0
* 2
* ---
* 3 0
* 2
* ---
* 1 1
* 2
* ---
* 0 1
*
* 12 --> 1100
*/
/*
* 15
* 2
* ---
* 7 1
* 2
* ---
* 3 1
* 2
* ---
* 1 1
* 2
* ---
* 0 1
*/
// int num = 0b1111;
// printf("%i\n", num);
/*
* 二进制 --> 十进制
* 1100 --> 十进制
* 规则: 系数 * 基数(索引)
* 系数: 每一位对应的值就是系数
* 基数:
* 例如从二进制转换到十进制, 那么二就是基数
* 例如从八进制转换到十进制, 那么八就是基数
* 索引:从最低位以0开始, 依次递增
*
* 1*2(3) + 1*2(2) + 0*2(1) + 0*2(0)
* 1 1 0 0
* 8 + 4 + 0 + 0
*/
/*
* 十进制转换八进制
* 规则: 除8取余, 余数倒叙
*
* 24
* 8
* ---
* 3 0
* 8
* ---
* 0 3
*
* 24 --> 30
*/
// printf("%i\n", 030);
/*
* 十进制转换十六进制
* 规则: 除16取余, 余数倒叙
* 24
* 16
* ---
* 1 8
* 16
* ---
* 0 1
*
* 24 --> 18
*/
// printf("%i\n", 0x18);
/*
* 八进制转换十进制
* 规则: 系数 * 基数(索引)
* 0*8(2) + 3 * 8(1) + 0*8(0)
* 0 3 0
*/
/*
* 十六进制转换十进制
* 规则: 系数 * 基数(索引)
* 1*16(1) + 8*16(0)
* 0x1 8
*/
/*
* 二进制转八进制
* 必须知道的是:
* 1.在八进制中最大的数字 7
* 2.三个二进制位最大能表示的数字就是7
* 1*2(2) + 1*2(1) + 1*2(0)
* 111
* 所以二进制在转换为八进制的时, 把三个二进制位看做一个八进制位即可
* 1 4
* 001 100 --> 14
*/
// 在C语言中不看怎么存, 只看怎么取
// printf("%o", 0b001100);
/*
* 二进制转十六进制
* 必须知道的是:
* 1.在十六进制中最大的数字 15
* 2.四个二进制位最大能表示的数字就是15
* 1*2(3) + 1*2(2) + 1*2(1) + 1*2(0)
* 1111
* 所以二进制在转换为十六进制的时, 把四个二进制位看做一个十六进制位即可
* 3 8
* 0011 1000 --> 0x38
*/
// printf("%x\n", 0b111000);
/*
* 二进制快速转换十进制
* 64 32 16 8 4 2 1
* 1 1 1 1 1 1 1
*/
return 0;
}
3、存储细节
#include <stdio.h>
int main()
{
/*
* 在计算机中 正整数和负整数的源码, 补码, 反码是有区别的
*/
/*
* 正整数的源码, 补码, 反码都是它的二进制, 三码合一
* 12 --> 1100
*
* 1100 就是12的源码
* 1100 就是12的反码
* 1100 就是12的补码
*
* int类型在计算机中占用4个字节
* 1个字节等于8位, 所以int类型总共占用32位
*
* 注意点: 在存储正整数的时候, 第一个(最高位)是符号位, 0代表是正数, 1代表是负数
* 0000 0000 0000 0000 0000 0000 0000 1100
*/
// int num = 12;
/*
* 负整数的源码,补码,反码
* 源码: 二进制, 将最高位变为1
* 1000 0000 0000 0000 0000 0000 0000 1100
* 反码: 除了符号位以外, 0变1, 1变0
* 1111 1111 1111 1111 1111 1111 1111 0011
* 补码: 反码+1
* 1111 1111 1111 1111 1111 1111 1111 0011
* 0000 0000 0000 0000 0000 0000 0000 0001
* ----------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 0100
*
* 注意点;
* 无论是正整数还是负整数, 存储在计算机中的都是补码
*
*
* 源码 -> 反码(0变1, 1变0) --> 补码(反码+1)
* 补码 -> 反码(补码-1) -> 源码(0变1, 1变0)
*/
// int num = -12
/*
* 1 + 1
* 0000 0000 0000 0000 0000 0000 0000 0001
* 0000 0000 0000 0000 0000 0000 0000 0001
* ----------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0010
*/
// printf("%i\n", 0b00000000000000000000000000000010);
/*
* 1 - 1 理解为 1 + (-1)
* 0000 0000 0000 0000 0000 0000 0000 0001 源码
* 1000 0000 0000 0000 0000 0000 0000 0001 源码
* ----------------------------------------
* 1000 0000 0000 0000 0000 0000 0000 0010 错误结果
*/
// printf("%i\n", 0b10000000000000000000000000000010);
/*
* 1 - 1 理解为 1 + (-1)
* 0000 0000 0000 0000 0000 0000 0000 0001 1补码
*
* 1000 0000 0000 0000 0000 0000 0000 0001 -1源码
* 1111 1111 1111 1111 1111 1111 1111 1110 -1反码
* 0000 0000 0000 0000 0000 0000 0000 0001
* ------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 1111 -1补码
*
*
* 0000 0000 0000 0000 0000 0000 0000 0001 1补码
* 1111 1111 1111 1111 1111 1111 1111 1111 -1补码
* -------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0000 0
*/
/*
* 4 - 6 --> 4 + (-6)
* 0000 0000 0000 0000 0000 0000 0000 0100 4补码
*
* 1000 0000 0000 0000 0000 0000 0000 0110 -6源码
* 1111 1111 1111 1111 1111 1111 1111 1001 -6反码
* 0000 0000 0000 0000 0000 0000 0000 0001
* -------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 1010 -6补码
*
*
*
* 0000 0000 0000 0000 0000 0000 0000 0100 4补码
* 1111 1111 1111 1111 1111 1111 1111 1010 -6补码
* 1111 1111 1111 1111 1111 1111 1111 1110 补码的结果
*
* 注意点: 当前计算出来的是正确结果的补码, 要想知道正确结果是多少, 需要将补码转换为源码
*
* 1111 1111 1111 1111 1111 1111 1111 1110 结果的补码
* 0000 0000 0000 0000 0000 0000 0000 0001
* -------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 1101 结果的反码
* 1000 0000 0000 0000 0000 0000 0000 0010 结果的源码 -2
*/
/*
0101
+ 0001
-----
0102 --> 0110
0110 --> 0102
- 0001 0001
---------------
0101
*/
return 0;
}
4、位运算
#include <stdio.h>
int main()
{
/*
* & 按位与
* | 按位或
* ~ 按位取反
* ^ 按位异或
*/
/*
* & 按位与
* 规则: 一假则假
* 在C语言中0代表假, 1代表真
* 9 & 3 = ?
*
* 0000 0000 0000 0000 0000 0000 0000 1001 // 9的补码
* & 0000 0000 0000 0000 0000 0000 0000 0011 // 3的补码
* -----------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0001 // 1
*
* 12 & 8
* 0000 0000 0000 0000 0000 0000 0000 1100
*& 0000 0000 0000 0000 0000 0000 0000 1000
* -----------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 1000
*/
// printf("%i\n", 9 & 3);
// printf("%i\n", 12 & 8);
/*
* | 按位或
* 规则: 一真则真
* 9 | 3 = ?
* 0000 0000 0000 0000 0000 0000 0000 1001 // 9的补码
*| 0000 0000 0000 0000 0000 0000 0000 0011 // 3的补码
* -----------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 1011
*/
// printf("%i\n", 9 | 3);
/*
* ~ 按位取反
* 规则: 真变假, 假变真
* ~9 = ?
* 0000 0000 0000 0000 0000 0000 0000 1001 // 9的补码
*~1111 1111 1111 1111 1111 1111 1111 0110 // 结果的补码
* 0000 0000 0000 0000 0000 0000 0000 0001
* -------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 0101 // 结果补码的反码
* 1000 0000 0000 0000 0000 0000 0000 1010 // 结果的源码 -10
*/
// printf("%i\n", ~9);
/*
* ^ 按位异或
* 规则: 相同为0, 不同为1
* 9 ^ 3 = ?
* 0000 0000 0000 0000 0000 0000 0000 1001 // 9的补码
*^0000 0000 0000 0000 0000 0000 0000 0011 // 3的补码
*-------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 1010 // 10
*/
printf("%i\n", 9 ^ 3);
return 0;
}
#include <stdio.h>
int main()
{
/*
* & 按位与
* 规则: 一假则假
* 规律: 任何一位和1相与, 结果还是原来的那一位
* 0000 0000 0000 0000 0000 0000 0000 0001 // 1的补码
*&0000 0000 0000 0000 0000 0000 0000 0001
* -----------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0001
*
* 0000 0000 0000 0000 0000 0000 0000 0000 // 0的补码
*&0000 0000 0000 0000 0000 0000 0000 0001
* -----------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0000
*
* 0000 0000 0000 0000 0000 1011 0000 0000 // 0的补码
*&0000 0000 0000 0000 0000 1111 0000 0000
* -----------------------------------------
* 0000 0000 0000 0000 0000 1011 0000 0000
*
*/
// int res1 = 1 & 1;
// printf("%i\n", res1);
// int res2 = 0 & 1;
// printf("%i\n", res2);
/*
*
* ^ 按位异或
* 规则: 相同为0, 不同为1
* 规律:
* 1.任何两个相同的值异或之后的结果都是0
* 2.任何一个数和0异或之后的结果还是那个数
* 3.任何一个数异或另外一个数两次之后, 结果还是那个数
*/
// int res1 = 5 ^ 5;
// printf("%i\n", res1);
// int res2 = 9 ^ 9;
// printf("%i\n", res2);
// int res1 = 5 ^ 0;
// printf("%i\n", res1);
// int res2 = 9 ^ 0;
// printf("%i\n", res2);
// int res1 = 18 ^ 5 ^ 5;
// printf("%i\n", res1);
// int res2 = 22 ^ 9 ^ 9;
// printf("%i\n", res2);
// 应用场景: 简单的加密
int pwd = 123456789;
int res = pwd ^ 998;
printf("加密之前: %i\n", pwd);
printf("加密之后: %i\n", res);
int res2 = res ^ 998;
printf("解密之后: %i\n", res2);
return 0;
}
install B小案例
#include <stdio.h>
void test1();
void test2();
int main()
{
/*
* 需求: 要求判断用户输入的数据是奇数还是偶数
*/
/*
// int num = 7;
// if(0 == (num % 2)){
// printf("%i是偶数\n", num);
// }else{
// printf("%i是奇数\n", num);
// }
*/
/*
int num = 12;
// 注意点: 三目是一个运算符, 所以必须有结果
// 也就是 结果A和结果B的位置放的必须是变量,常量, 表达式
// 如果放的是一个函数, 那么这个函数必须有返回值
// 0 == (num % 2) ? printf("%i是偶数\n", num) : printf("%i是奇数\n", num);
// 0 == (num % 2) ? test1() : test2();
// printf函数的返回值, 是我们打印了多少个字符
// int res = printf("李"); // 如果放的是中文, 是2还是3不确定, 根据当前的编码方式GBK UTF-8
// printf("res = %i\n", res);
*/
/*
* 8 偶数
* 1000
* & 0001
* -------
* 0000 --> 0
*
* 12 偶数
* 1100
* & 0001
* -------
* 0000 --> 0
*
*
* 9 奇数
* 1001
* & 0001
* -------
* 0001 --> 1
*
* 13 奇数
* 1101
* & 0001
* -------
* 0001 --> 1
*/
int num = 4;
// if(1 == (num & 1)){
// printf("%i是奇数\n", num);
// }else{
// printf("%i是偶数\n", num);
// }
if(num & 1){
printf("%i是奇数\n", num);
}else{
printf("%i是偶数\n", num);
}
return 0;
}
void test1(){
printf("test1\n");
}
void test2(){
printf("test2\n");
}
#include <stdio.h>
int main()
{
/*
* << 左移
* >> 右移
*/
/*
9 << 1
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
000 0000 0000 0000 0000 0000 0000 10010 // 9的补码
9 << 2
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
00 0000 0000 0000 0000 0000 0000 100100// 9的补码
移动规则: 从符号位开始整体移动, 多余的砍掉, 缺少的用零补
计算规律: 一个数左移几位就是 这个数乘以2的多少次幂
9 * 2(1) = 18
9 * 2(2) = 36
应用场景: 在企业开发中一旦想要某个数乘以2的多少次幂, 就要想到左移
*/
// printf("%i\n", 9 << 1);
// printf("%i\n", 9 << 2);
/*
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
00000 0000 0000 0000 0000 0000 0010 010
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
000000 0000 0000 0000 0000 0000 0010 01
移动规则: 除符号位以外整体右移, 多出来的砍掉, 缺少的用符号位填充
计算规则: 一个数右移几位就是 这个数除以2的多少次幂
36 / 2(1) = 18
36 / 2(2) = 9
应用场景: 在企业开发中一旦想要某个数除以2的多少次幂, 就要想到右移
*/
// printf("%i\n", 36 >> 1);
// printf("%i\n", 36 >> 2);
/*
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
00000 0000 0000 0000 0000 0000 0000 100 --> 4
*/
printf("%i\n", 9 >> 1);
/*
* 1000 0000 0000 0000 0000 0000 0000 1001 // -9源码
* 1111 1111 1111 1111 1111 1111 1111 0110 // -9反码
* 1111 1111 1111 1111 1111 1111 1111 0111 // -9补码
*
0000 0000 0000 0000 0000 0000 0000 0000 // 位置参考
11111 1111 1111 1111 1111 1111 1111 011 // 右移完毕的结果, 补码
0000 0000 0000 0000 0000 0000 0000 0001
11111 1111 1111 1111 1111 1111 1111 010 // 右移完毕的结果, 反码
10000 0000 0000 0000 0000 0000 0000 101 // 右移完毕的结果, 源码
*/
printf("%i\n", -9 >> 1);
// printf("%i\n", -9 / 2);
return 0;
}
#include <stdio.h>
int main()
{
/*
* 要求将用户输入的整数, 以二进制的形式输出
* 0000 0000 0000 0000 0000 0000 0000 1001
* 0000 0000 0000 0000 0000 0000 0000 0001
*/
int num = 9;
// int temp = (num >> 3) & 1;
// printf("temp = %i\n", temp);
// temp = (num >> 2) & 1;
// printf("temp = %i\n", temp);
// temp = (num >> 1) & 1;
// printf("temp = %i\n", temp);
// temp = (num >> 0) & 1;
// printf("temp = %i\n", temp);
// for(int i = 0; i < 32; i++){
// int temp = (num >> 31 - i) & 1;
// printf("%i", temp);
// }
char ch = 'a';
// printBinary(num);
printBinary(ch);
printf("%i\n", 0b01100001);
return 0;
}
void printBinary(char value){
// int 4 --> 32
// char 1 --> 8
// 在企业开发中 , 数字我们称之为魔鬼
int len = sizeof(value) * 8;
for(int i = 0; i < len; i++){
int temp = (value >> (len - 1 - i)) & 1;
printf("%i", temp);
if(((i+ 1) % 4) == 0){
printf(" ");
}
}
printf("\n");
}
#include <stdio.h>
int main()
{
/*
* 需求: 交换两个变量的值
*
* int a = 10; int b = 20;
*/
int a = 10;
int b = 20;
printf("交换之前a = %i, b = %i\n", a, b);
// int temp = a;
// a = b;
// b = temp;
// printf("交换之后a = %i, b = %i\n", a, b);
// a = a + b; // a = 10 + 20; a = 30;
// b = a - b; // b = 30 - 20; b = 10;
// a = a - b; // a = 30 - 10; a = 20;
// printf("交换之后a = %i, b = %i\n", a, b);
// a = a ^ b;
// b = a ^ b; // b = a ^ (b ^ b); b = 10;
// a = a ^ b; // a = (a ^ a) ^ b ; a = 20;
b = a ^ b;
a = a ^ b; // a = a ^ a ^ b; a = b; a = 20;
b = a ^ b; // b = a ^ b ^ b; b = a; a = 10;
printf("交换之后a = %i, b = %i\n", a, b);
return 0;
}
#include <stdio.h>
int main()
{
/*
* 变量的内存分析
* 1.内存是连续的
* 2.内存地址从打到小
* 3.计算机会从内存地址大的开始分配内存(内存寻址从大到小)
* 也就是说先定义的变量内存地址大于后定义的变量
*
*/
/*
* 0000 0000 0000 0000 0000 0000 0000 1001 // 补码
*
* 规则: 由于内存寻址是从大到小, 所以存储补码也是从大到小(也就是从高位开始存储)
*/
int num1 = 9;
int num2 = 7;
printf("num1 %p\n", &num1);
printf("num2 %p\n", &num2);
char *ch = &num1;
for(int i = 0; i < 4; i++){
printf("%i\n", ch[i]);
}
printf("%p\n", ch++);
printf("%p\n", ch++);
printf("%p\n", ch++);
printf("%p\n", ch++);
return 0;
}