一、预处理
1、预处理主要包括四种类型 文件包含、宏替换、条件编译、布局控制 #pragma pack(n);//n:1、2、4、8、16,n只能为2的指数 #pragma pack(push,n); #pragma pack(pop); //成对处理 2、编译器预定义宏 __DATE__ __FILE__ __LINE__ __TIME__ __FUNCTION__(函数名) 二、字节对齐 字节对齐的细节和编译器实现相关,但一般而言,满足三个准则: 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。 三、函数声明前的压栈声明: _stdcall _cdecl _cdecl:C/C++默认约定,由调用者清理堆栈,参数自右至左入栈(支持变参函数) _stdcall:Win32 API调用约定,由被调用函数清理堆栈,参数自右至左入栈(不支持变参函数),生成代码较短 void _cdecl fun(int a,char b); 等价于 void fun(int a,char b); fun(0,'a');//这句函数调用语句的反汇编为 00411D5E push 61h 00411D60 push 0 00411D62 call fun (4112FDh) 00411D67 add esp,8 修改函数声明 void _stdcall fun(int a,char b); fun(0,'a');//反汇编代码为 00411D5E push 61h 00411D60 push 0 00411D62 call fun (411302h) 比_cdecl少了一句清理堆栈的语句。 由于参数是自右往左入栈的,那现面的代码结果应不难猜出: int i=0; cout<<i++<<++i<<endl; result:12 解析:先将参数++i,i++入栈,并进行相关操作后,再调用<<函数。 也就是在函数调用前i的值已经是2了,不过因为调用cout<<i++时, 使用的参数是临时变量,所以仍为1 用伪码表示下面的代码为: int i=0; i = i+1; temp = i; i = i+1; push i; push temp; call << call << 四、default的说明 default与其他标号同级别。 在switch语句中加入default语句之后,会在switch后生成如下代码, 00411D87 jmp main+79h (411DB9h) (地址为default:所在地址) 而如果不加入default,则也会生成类似代码,不过地址为switch语句结尾处。 也就是: switch(2) { default:cout<<"default"<<endl; case 2:cout<<"2"<<endl; } 该语句只输出:2 五、操作符、表达式 int i=0; i += ++i; cout<<i<<endl; 结果:2 六、虚函数几点注意 析构函数要定义为虚函数,构造函数不能定义为虚函数 虚函数的默认值不会被覆盖 内联函数不能被定义为虚函数 class A { public: virtual void get(int a=1){cout<<a<<endl;} }; class B:public A { public: virtual void get(int a=2){cout<<a<<endl;} }; void main(int argc,int **argv) { A *a = new B(); a->get(); //cout:1 } 七、explicit关键字的作用 修饰构造函数 防止隐式转化 是针对单参数的构造函数而言 多于2个以上不会隐式转化 八、static成员函数 不能访问非static成员,却可以进行如下操作 class A { public: static void get(A& a){a.a;a.ab();} private: int a; void ab(){}; }; 等续....