01-进制转换-基本概念
如何输出十进制 %i %d, 以十进制的方式输出数据
在编程中想表示二进制,需要在数字前面加上0b
%o 就是以八进制的形式输出
在编程中想表示八进制,需要在数字前面加上0
%x 就是以十六进制的形式输出
在编程中想表示十六进制,需要在数字前面加上0x
注意: 在C语言中没有以二进制输出的占位符
02-进制转换
1.十进制 转 二进制
规律 : 用需要转换的十进制 除以2 取余数 , 然后倒数
13 / 2 = 6 余 1
6 / 2 = 3 余 0
3 / 2 = 1 余 1
1 / 2 = 0 余 1
-------- = 1101
2. 二进制 转 十进制
规律 : 从低位数开始, 用低位 乘以 2的 多少次幂, 幂数从0开始,然后再相加
二进制 : 1101
1 * 2(0) = 1
0 * 2(1) = 0
1 * 2(0) = 4
1 * 2(3) = 8
1 + 0 + 4 + 8 = 13
口诀
1 1 1 1
8 4 2 1
3. 二进制 转 八进制
规律 : 三个二进制位 代表一个八进制位, 只需要将3个二进制位 转换为十进制, 之后再将所有的结果连接起来
1001 转换为 00 001 001
0 1 1
------------------------
011 == 0 * 8 + 1 * 8 + 3 = 11(十进制)
4. 二进制 转 十六进制
规律 : 四个二进制位代表一个十六进制位, 只需要将4个二进制位转换成10进制, 之后再将所有的结果连接起来
0001 1011
--------
1 b = 0x1b
03-原码补码反码
为什么要有 原码 / 反码 / 补码 ? 主要是为了方便计算机计算
其实二进制的第一位 是二进制的符号位
如果该位是0 代表这个数 是正数
如果该位是1 代表这个数 是负数
正数
总之一句话, 正数的原码\补码\反码 都是一样的,三码合一
负数
反码 : 符号位不变, 其他位取反(0变1,1变0)
补码 : 反码 + 1就是补码
9的二进制
正数
0000 0000 0000 0000 0000 0000 0000 1001 (原码)/反码/补码
负数
1000 0000 0000 0000 0000 0000 0000 1001 (原码)
1111 1111 1111 1111 1111 1111 1111 0110 (反码)
1111 1111 1111 1111 1111 1111 1111 0111 (补码)
// 补码的深入
1 - 1 = 1 + (-1)
原码 计算结果 = -2
反码 计算结果 = -0
补码 计算结果 = 0
04-位运算
位运算 (和二进制相关)
1.& 按位与
规律 : 一假则假, 1真 0假
规律 : 任何数和 1 相& 得到的结果还是那个数
1001 & 1111 = 1001
2.| 按位或
规律 : 一真则真
3.^ 按位异或
重点 : 异或的结果 和 参与运算的顺序没有关系
规律1 : 不相同为1, 相同为0 (异或)
1001 ^ 0101 = 1100
规律2 : 相同的两个数 异或 等于0
5 ^ 5 = 0101 ^ 0101 = 0
规律3 : 任何数 和 0 异或上0 结果不变
5 ^ 0 = 0101 ^ 0000 = 0101 = 5
规律4 : 任何一个数 异或上 同一个数两次, 结果不变
9 ^ 5 ^ 5 = ? == 9 ^ 0 = 9
~ 取反
计算机是以补码形式存在的
1.先拿到数 的原码 [进行一个取反] 在 -1 变成反码 再将反码 转为原码
一 . 9 原码 0000 0000 0000 0000 0000 0000 0000 1001
二 . 取反的得到补码 -->
1111 1111 1111 1111 1111 1111 1111 0110 的到的补码
三 . 再将 补码 -1变成反码 -->
1111 1111 1111 1111 1111 1111 1111 0101
四 . 再将反码取反变回原码 --->
1000 0000 0000 0000 0000 0000 0000 1010 == -10
~9 = -10
原码 --> 反码(取反) -->(补码)+1
补码 --> 反码(-1) --> 原码(取反)
05-左移右移
左移 <<
规律 : 左移 就是左移的数乘以2的移动次幂
例如
9 << 1 = 9 * 2(1) = 18
9 << 2 = 9 * 2(2) = 36
注意点 :
由于左移运算,被移动的数 最高位会被抛开(移除),所以左移有可能会改变一个数的正负性
右移 >>
规律 : 右移 就是右移的数除以2的移动次幂
9 >> 1 = 9 / 2(1) = 4;
9 >> 1 = 9 / 2(2) = 2;
注意 : 负数的左移右移 是补码在移动,因为负数都是以补码的形式存储在内存中的
应用场景 : 如果想让某一个数 乘以2的多少次幂,或者除以2的多少次幂,最高运算方式就是左移右移
06-位运算-练习1
使用按位与 &
任何数和 1 相& 得到的结果还是那个数
0000 0000 0000 0000 0000 0000 0000 1001
&0000 0000 0000 0000 0000 0000 0000 0001
----------------------------------------
// 1.让9的二进制向右移31位,就可以获取到9的最高位的二进制,然后让9的二进制的最高位 和1相与,那么就可以获得9的最高位
// 2.让9的二进制向右移动30位,就可以获取9二进制第二位
// 3.以此类推,直到0位置
技巧
1.任何数 与 1相& 都是那个数
2.利用位移 取出每一位
07-位运算-练习2
1001 9
1010 10
1011 11
1100 12
通过观察,
我们发现如果是偶数,那么二进制的最后1位是0,
如果是奇数,那么二进制的最后1位是1.
if ((num & 1) == 1) {
printf("奇数");
}
else
{
printf("偶数\n");
}
08-位运算-练习3
/*
不相同为1, 相同为0 (异或)
*/
/*
a = a ^ b;
b = a ^ b; // a ^ b ^ b = a ^ 0 = a
a = a ^ b; // a ^ b ^ a = b ^ 0 = b
*/
#pragma 开发简单加密
// 用户密码, 纯数字
int pwd = 123;
// 对用户密码 进行简单加密
int result = pwd ^ 456;
// 对用户密码进行解密
result = result ^ 456;
09-变量内存分析
主要定义变量, 系统就会开辟一块存储空间 给我们的变量存储数据,内存寻址是从大到小
越先定义的变量,内存地址越大
注意 : 由于内存寻址是从大到小, 所以存储数据也是从大到小的存储(先存储二进制的高位,再存储低位)
高位 --> 低位
0000 0000 0000 0000 0000 0000 0000 1001
10-char类型-基本概念
计算机只能识别0和1 'a' 通过ASCII码表 获取一个值 转换成二进制
在C语言中, 不看怎么存, 只看怎么取
char类型 在某些情况下 可以当做整型来用
如果对内容要求特别严格,而且需要存储的整数 不超过char类型的取值范围,那么就可以使用char类型来代替int类型
-2(7)~2(7) - 1 = -128 ~ 127
char c =128; // 打印出来 -128 因为超出了范围
11-char类型-练习
大写 ASCII码 65 - 90
小写 ASCII码 97 - 122
差值 ('a' - 'A') 97 - 65 = 32;
12-类型说明符
类型说明符:
1.说明长度的 (它可以用于修改 类型 所占用的存储空间的大小)
short;
输出 %hi hd // 2个字节 == -2(15)~2(15)-1
long;
输出 %li ld // (64位占8个字节) 32 位long 占用4个字节 , long long 占用 8个字节 == -2(63) ~ 2(63)-1
long long;
输出 %lli %lld // 8个字节 == -2(63) ~ 2(63)-1
用于说明数据类型, 一般情况下 和 int 配合使用
2.说明符号位 (它可以用于修改符号位)
unsigned; (无符号) 输出 %u . 取值 整数 和 零
signed; (有符号的) 取值 正数 和 零 以及 负数
如果给变量加上修饰符 signed 代表当前变量的取值可以是 正数 / 负数 /零
如果给变量加上修饰符 signed 就代表把 二进制的最高位作为符号位
而且默认情况下所有变量都是有符号的(signed)
3.不同类型的说明符 可以混合使用
unsigned short
signed long
注意 : 相同类型的 说明符 不能同时在一起使用
13-数组-基本概念
/*
数组的定义格式 :
数据类型 变量名称;
数据类型 数组名称[数据的个数];
元素类型 数组名称[元素个数];
元素类型:就是数组中需要存储的数据类型 , 一旦确定, 数组中就只能存储该类型的数据
元素个数: 就是数组中能够存储的数据(元素)的个数
*/
14-数组-初始化
数组的部分\完全初始化
部分初始化 [1)默认从0开始初始化 -- 2)如果"在部分初始化中",对应的内存没有被初始化, 那么默认是0]
int scores1[3] ={11,22};
完全初始化 [1) 依次将{}中的每一个值 赋值给数组中的每一个元素 -- 2)并且从0开始赋值]
int scores[5] = {99,88,77,66,100};
注意1:如果没有对数据进行初始化(完全和部分),那么不要随便使用数组中的数据,可能是一堆垃圾数据(随机值)
int scores2[3]; // 0 = 32767 1 = 0 2 = 0
注意2: 定义数组的时候,数组的元素个数不能使用变量,如果使用变量,那么数组中 是一些随机值
int num = 3; int scores3[num]; // 0 = 1606416240 1 = 32767 2 = 3611
(报错)注意3: 不建议使用变量定义数组,如果使用了变量定义数组, 作为数组的元素个数,不初始化的情况下是随机值,如果初始化会直接报错
int num1 = 3;int scores4[num1] = {11,12};
注意4: 如果定义的同时进行初始化,那么元素的个数可以省略
省略之后, 初始化赋值几个数据,那么数组的长度就是几,也就是说数组将来就能存储 几个数据
int scores5[] = {1,3};
(报错)注意5: 如果定义数组时没有进行初始化, 那么不能省略元素个数
int scores6[];
可通过[索引] = 的方式, 给指定索引的元素赋值
int scores7[101] = {[99]=1,[100]=5};
(报错)注意6: 只能在定义的同时 利用{}进行初始化,如果是先定义 那么就不能使用{}进行初始化
如果先定义 那么就不能再进行整体赋值, 只能单个赋值
int scores8[3];
scores8 = {1,3,4};
15-数组-遍历
// 注意 : 在遍历数组的时候, 尽量不要把遍历的次数写死
// 遍历多少次应该 由数组来决定 , 也就是说遍历多少次 应该通过数组计算得出
/*
printf("scores = %lu\n",sizeof(scores)); // 计算出数组中占用的总字节数
printf("socre[0] = %lu\n",sizeof(scores[0])); // 计算出数组中某一个元素占用的字节数
printf("一个有多少个元素 : %lu\n",sizeof(scores) / sizeof(scores[0]));
*/
// 动态计算数组的元素个数
int length = sizeof(scores) / sizeof(scores[0]);