1、拆分字节
1.1、全部拆分
一个字节可以拆分成 8 个 bit,下面的方法拆分出的字节储存在 m 数组里,data 是要拆分的字节。
int jk(int data){
int m[8];
int i;
for(i=0; i < 8; i++)
{
m[i]=data & 0x01;
data = data >> 1;
}
return 0;
}
具体效果是,4 会被拆分成 00100000,并储存在 m 数组里。简单来说就,就是将 4 转换成二进制数。不同于 c/c++ 自带的函数,这个方法是把 4 拆分成单个 bit 保存下来,而自带的函数则会把 4 转换成一个二进制字符串。4 的二进制表示是 00000100,而这个方法拆分得到的数据和原来比较是相反的,也就是 00000100 会变成 00100000。
1.2、部分拆分
这个方法是拆分出特定位置的数字。
unsigned int a = 0x12345678;
((x >> 0) & 0x000000ff);/* 获取第 0 个字节,结果是 0x78 */
((x >> 8) & 0x000000ff);/* 获取第 1 个字节,结果是 0x56 */
((x >> 16) & 0x000000ff);/* 获取第 2 个字节,结果是 0x34 */
((x >> 24) & 0x000000ff);/* 获取第 3 个字节,结果是 0x12 */
((0b101 >> 2) & 0x01);/* 获取第 2 个字节,结果是 0x34 */
假设要把 a 按字节拆分。两位数字占了 8 个 bit,取第 n 个字节,就右移 8n 个 bit,然后和 0xff 按位与,就可以得出结果。0xff 不是随便取的,8 个 bit 最大的数字是 255(十进制),换成十六进制就是 ff。
同理,把 a 按单个数字拆分,一位数字占了 4 个 bit,取第 n 个字节,就右移 4n 个 bit,然后和 0x1111 按位与。把数字换成二进制,自己手动运算一下就知道原理了。
2、c/c++ 内存知识
现代操作系统对每个进程都分配了完整的虚拟内存地址空间。进程会把整个地址空间分成多个区间来使用。
虚拟内存技术使得每个进程都可以“独占”整个内存空间,地址从零开始,直到内存用完。 每个进程都将这部分空间(从低地址到高地址)分为六个部分:
- TEXT 段:程序的代码,以及常量。这部分内存固定大小,只读的。对常量的赋值将产生 segment fault 错误。
- DATA 段:又称 GVAR,初始化为非零的全局变量。
- BSS 段:初始化为 0 或未初始化的全局变量和静态变量。可以认为 BSS 段中的所有字节都是 0。它们都会被初始化为 0,同时类的成员变量也会被初始化为 0,但编译器不保证局部变量的初始化。
- HEAP(堆空间):动态内存区域,使用 malloc 或 new 申请的内存。
- 未使用的内存。
- STACK(栈空间):局部变量、参数、返回值都存在这里,函数调用开始会参数入栈,局部变量入栈,调用结束后,按照先进后出的顺序出栈。
堆空间和栈空间的大小是可变的。堆空间从下往上生长,栈空间从上往下生长。(这句话不一定正确,要看操作系统如何实现内存管理)
栈(STACK)是从上到下(高地址到低地址)分配的,函数的局部变量的空间是在进入函数体后才分配的。
上述和CPU端模式也有关系!小端字节序的,低位存的是低字节。大端字节序的,低位存的是高字节。
变量所占内存的回收方式取决于该变量的存储类型(storage class)。
- 局部变量,存储在栈空间内。它占用的空间会在函数调用结束后自行释放,如果调用太多次,栈来不及释放,会造成内存溢出。
- 全局变量,存储在 DATA 段或者 BSS 段,它的空间是始终存在的,直至程序结束运行。
- 如果是 new 或者 malloc 得到的空间,它存储在 HEAP(堆)中,除非手动 delete 或 free,否则空间会一直占用直至进程结束。
参考文章:
https://blog.csdn.net/xtuwz/article/details/79532770
https://harttle.land/2015/07/22/memory-segment.html
https://harttle.land/2015/07/22/memory-segment.html
3、提高 c/c++ 效率的方法
下面是运算效率对比。
移位 > 赋值 > 大小比较 > 加法 > 减法 > 乘法 > 取模 > 除法
- 尽量用移位操作代替其他操作,编译器会帮我们优化,这个一点并不必要,还是要看具体情况。
- 尽量用指针运算代替数组索引,产生的代码往往又快又短。与数组索引相比,指针能使代码更快,占用更少。使用多维数组时差异更明显。
- 使用或设置尽量小的数据类型,这样使生成的代码的数量减少很多,执行速度提高。
- 如果说数据量有限,可以采用查表的方式,执行速度会快一些。
- 要充分利用 CPU 的指令缓存,就要充分分解小的循环。循环很小时,可以拆分。
- 按数据类型的长度排序,长的在前,短的在后。把结构体填充成最长类型长度的整倍数。
- 尽量用自减替代自加,内存占用会少一点。
- 使用 do while 循环编译后生成的代码的长度短于 while 循环。
- 循环少嵌套。
- Switch 语句中根据发生频率来进行 case 排序,频率高的在前。
- 有分支的语句也保持频率高的在前。
- 大批量内存拷贝,用 memcpy 代替赋值语句
4、杂项
- 当数据不均匀,而又对数据十分敏感的时候,映射是一个好办法。例如触控板绝对模式发送的绝对位置,映射处理之后,更加均匀并且可以减少错误数据的影响。
- a^b=b^a
a^(b^c)=(a^b)^c
a^b^b=a
余数定理,(ab)%c=(a%c)(b%c)%c - 结论:byte、short、char 做混合运算的时候,各种先转换成 int 再做运算。
- 期望是概率的倒数
- 两个数的最小公倍数可以代替原来的两个数。最小公倍数的两个数为 x 和 y,他们的最大公约数为 p,最小公倍数为 q。则 xy=pq
更新日志
2022/7/25:提高效率部分补充些内容。