存档在 ‘技巧’ 分类

位运算总结

2017年4月8日

基础

位操作符

符号 含义 规则
& 两个位都为1时,结果为1
| 有一个位为1时,结果为1
^ 异或 0和1异或0都不变,异或1则取反
~ 取反 0和1全部取反
<< 左移 位全部左移若干位,高位丢弃,低位补0
>> 算术右移 位全部右移若干位,,高位补k个最高有效位的值
>> 逻辑右移 位全部右移若干位,高位补0

注意:

1、位运算只可运用于整数,对于float和double不行。

2、另外逻辑右移符号各种语言不太同,比如java是>>>。

3、位操作符的运算优先级比较低,尽量使用括号来确保运算顺序。比如1&i+1,会先执行i+1再执行&。

 

应用实例

很棒的应用实例,你可以mark一下,方便以后对照使用。

1、混合体

位运算实例

位运算 功能 示例
x >> 1 去掉最后一位 101101->10110
x << 1 在最后加一个0 101101->1011010
x << 1 | 1 在最后加一个1 101101->1011011
x | 1 把最后一位变成1 101100->101101
x & -2 把最后一位变成0 101101->101100
x ^ 1 最后一位取反 101101->101100
x | (1 << (k-1)) 把右数第k位变成1 101001->101101,k=3
x & ~ (1 << (k-1)) 把右数第k位变成0 101101->101001,k=3
x ^(1 <<(k-1)) 右数第k位取反 101001->101101,k=3
 x & 7 取末三位 1101101->101
x & (1 << k-1) 取末k位 1101101->1101,k=5
x >> (k-1) & 1 取右数第k位 1101101->1,k=4
x | ((1 << k)-1) 把末k位变成1 101001->101111,k=4
x ^ (1 << k-1) 末k位取反 101001->100110,k=4
x & (x+1) 把右边连续的1变成0 100101111->100100000
x | (x+1) 把右起第一个0变成1 100101111->100111111
x | (x-1) 把右边连续的0变成1 11011000->11011111
(x ^ (x+1)) >> 1 取右边连续的1 100101111->1111
x & -x 去掉右起第一个1的左边 100101000->1000
x&0x7F 取末7位 100101000->101000
x& ~0x7F 是否小于127 001111111 & ~0x7F->0
x & 1 判断奇偶 00000111&1->1

安装astyle格式化代码插件[VS12]

2014年10月16日
  • 1、工具-》扩展和更新,搜astyle插件,下载安装重启,当前是2.0版本。
  • 2、工具-》选项-》AStyle Formatter-》Edit,填入下面的,点击save,确定。
  • --style=allman --indent=spaces=4 --indent-switches --indent-cases --indent-namespaces --break-blocks --pad-oper --unpad-paren --delete-empty-lines --convert-tabs --mode=c

    说明:
    –indent=spaces=4 行缩进用4个空格
    –indent-switches switch 与case不同列,case缩进
    –indent-cases 缩进case下面的语句
    –break-blocks 空行分隔无关块
    –delete-empty-lines 删除多余空行
    –pad-oper 操作符两端出入空格
    –unpad-paren 移除括号两端多余空格
    –convert-tabs tab转空格

  • 3、工具-》选项-》环境-》键盘-》显示命令包含 下面的框里输入astyle,按快捷键里按住alt+f组合键,确定。
  • 4、以后可以在源码编辑框中使用 alt+f 格式化源码了。

LNK2005原因与解决办法

2014年10月5日

代码为LNK2005的链接错误一般是某个头文件被多次包含导致,有时候在编译阶段也会由头文件多个地方包含引起错误,一般解决方法如下:

#ifndef _XX_头文件.H
#define _XX_头文件.H
int A;
#endif

但是针对链接2005出错则不行,可以的解决方案是:

  • 1.在源文件中定义,在头文件中extern中声明;
  • 2.在头文件中定义函数,并在前面加上MY_INLINE,这样普通函数也会被内联或者静态处理。
  • #ifndef MY_INLINE
    #if defined __cplusplus
        #define MY_INLINE inline
    #elif (defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64 || defined WINCE) && !defined __GNUC__
        #define MY_INLINE __inline
    #else
        #define MY_INLINE static
    #endif

减少分支预测提高程序性能

2014年10月4日
z_int32 nearZ = 0;  
for( z_int32 j = 0; j < sw; j++, alpha -= z_pi_f )  
{  
    if( alpha < NEARZERO && alpha > -NEARZERO ) nearZ = j;  
    else if( j & 1 ) sum -= horz[j] / alpha;  
    else sum += horz[j] / alpha;  
}  

此代码有几个条件,一个是位于0附近的特殊处理,另外是循环变量的奇偶判断。
耗时:1312.68457 ms

将if拆成更多的for,减少for循环的分支预测:

z_float32 alpha2 = alpha1 - z_pi_f;  
z_int32 j;  
for( j = 0; alpha1 > z_pi_f; j+=2, alpha1 -= z_2pi_f) sum += horz[j] / alpha1;  
if( alpha1 > -NEARZERO && alpha1 < NEARZERO ){ nearZ = j; j += 2; alpha1 -= z_2pi_f; }  
for( ; j < sw; j+=2, alpha1 -= z_2pi_f ) sum += horz[j] / alpha1;  
  
for( j = 1; alpha2 > z_pi_f; j+=2, alpha2 -= z_2pi_f) sum -= horz[j] / alpha2;  
if( alpha2 > -NEARZERO && alpha2 < NEARZERO ){ nearZ = j; j += 2; alpha2 -= z_2pi_f; }  
for( ; j < sw; j+=2, alpha2 -= z_2pi_f ) sum -= horz[j] / alpha2;  

先拆奇偶,然后拆0附近的循环,这样下来的效果是
耗时:918.65375 ms

高性能I/O设计的Reactor和Proactor模式

2014年4月8日

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步。

     同步和异步是针对应用程序和内核的交互而言的。

  1. 同步指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪;
  2. 异步是指用户进程触发IO操作以后便开始做自己的事情,而当I/O操作已经完成的时候会得到I/O完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据I/O操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式。
  3. 阻塞方式下读取或者写入函数将一直等待;
  4. 非阻塞方式下,读取或者写入函数会立即返回一个状态值;

  一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞I/O

   同步阻塞I/O

      在此种方式下,用户进程在发起一个I/O操作以后,必须等待I/O操作的完成,只有当真正完成了I/O操作以后,用户进程才能运行。

   同步非阻塞I/O:

      在此种方式下,用户进程发起一个I/O操作以后边可返回做其它事情,但是用户进程需要时不时的询问I/O操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。

   异步阻塞I/O

      此种方式下是指应用发起一个I/O操作以后,不等待内核I/O操作的完成,等内核完成I/O操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!

   异步非阻塞I/O:

       在此种模式下,用户进程只需要发起一个I/O操作然后立即返回,等I/O操作真正的完成以后,应用程序会得到I/O操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的I/O读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种I/O模型。

       搞清楚了以上概念以后,我们再回过头来看看,Reactor模式和Proactor模式。

       首先来看看Reactor模式,Reactor模式应用于同步I/O的场景。我们分别以读操作和写操作为例来看看Reactor中的具体步骤:

读取操作:

1. 应用程序注册读就绪事件和相关联的事件处理器

2. 事件分离器等待事件的发生

3. 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器

4. 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理

写入操作类似于读取操作,只不过第一步注册的是写就绪事件。下面我们来看看Proactor模式中读取操作和写入操作的过程:

读取操作:

1. 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。

2. 事件分离器等待读取操作完成事件

3. 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作,并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。

4. 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。

Proactor中写入操作和读取操作,只不过感兴趣的事件是写入完成事件。

      从上面可以看出,Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备.

      综上所述,同步和异步是相对于应用和内核的交互方式而言的,同步 需要主动去询问,而异步的时候内核在IO事件发生的时候通知应用程序,而阻塞和非阻塞仅仅是系统在调用系统调用的时候函数的实现方式而已。

来源:网络收集

VS2012中调试DLL

2014年4月5日

DLL不能单独运行,必须依靠其他测试程序才能使用。以刚刚写的一个循环队列DLL作为素材,详细说明一下步骤:

1.假设你的DLL工程为CircularQueue,测试dll的工程为CircularQueueTest。为了更好的调试,这两个工程建立在一个工程中,这个很简单,在“解决方案资源管理器”里的工程上“右键”选择“添加->新建项”;
2.为CircularQueueTest添加dll引用,“右键”CircularQueueTest工程,选择“通用属性->框架和引用->添加新引用”,就会看见这个dll工程了,添加后确定。
QQ图片20140405223740
3.拷贝CircularQueueTest.h到CircularQueueTest的工作目录中;
4.在代码中添加dll的使用,比如添加#pragma comment(lib, “CircularQueue.lib”)与头文件包含#include “CircularQueue.h”
6.打开VS2012菜单栏的类视图,在上面选择类,下半部分选择函数来插入断点;
1
7.调试步骤和其他程序调试一样。

利用 Visual Assist自动生成注释

2014年4月3日

1.测试环境

VS2012

2.使用步骤

  1. 在VS工具栏中找到 Visual Assist插件的工具区,找到“Insert VA Snippet ”;
  2. 选择“Edit VA Snippet”,会弹出一个对话框;
  3. 点击2中对话框菜单栏的“new”
  4. 可以按照如下方式填写,也可以自己定制不同描述
    title里填写:函数注释
    shortcut:/=
    Description:函数注释
    在最下面空白内容输入:

    /*==================================================================
     *    函数名    :    
     *    功能    :   
     *    输入参数:
     *    返回值    : 
     *    作者    :   
     *    日期    :
     *    修改记录:
    /*==================================================================*/
  5. 需要给函数插入注释时,敲入“/=”,选择函数注释即可。