通过汇编代码研究C++

首先树立以下几个概念:

  • 编译器 compiler
  • 链接器 linker
  • 编译时期 compile time
  • 运行时期 runtime
  • 数据段 data segment/ds
  • 代码段 code segment/cs
  • 语法 syntax
  • 语义 semantics
  • 栈 stack
  • 堆 heap

全局变量

int global_var = 5;

int
main(int argc,char** argv)
{
static int i=3;
i=7;
return 0;
}
PUBLIC          ; global_var
_DATA SEGMENT

;静态变量和全局变量在数据段分配
DD 05H          ; global_var
DD 03H
_DATA ENDS
PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
push ebp
mov ebp, esp

; 直接对数据段内存赋值
mov DWORD PTR , 7 
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

类的静态成员变量

class MyClass
{
public:
       int static       STATIC_VAR;
};

int MyClass::STATIC_VAR = 7;

int
main(int argc,char** argv)
{
MyClass::STATIC_VAR = 6;
return 0;
}
PUBLIC         ; MyClass::STATIC_VAR
_DATA SEGMENT

;类的静态成员同样在数据段分配分配内存
DD 07H         ; MyClass::STATIC_VAR
_DATA ENDS


PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
push ebp
mov ebp, esp

;直接对数据段内存赋值
mov DWORD PTR , 6 ; MyClass::STATIC_VAR
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

枚举

enum COLOR{
cl0,cl1,cl2,cl3,cl4,cl5,cl6,cl7,cl8,cl9,cl10,cl11,cl12,cl13,cl14,cl15,
cl16,cl17,cl18,cl19,cl20,cl21,cl22,cl23,cl24,cl25,cl26,cl27,cl28,cl29,cl30,
cl31,cl32,cl33,cl34,cl35,cl36,cl37,cl38,cl39,cl40,cl41,cl42,cl43,cl44,cl45,
cl46,cl47,cl48,cl49,cl50,cl51,cl52,cl53,cl54,cl55,cl56,cl57,cl58,cl59,cl60,
cl61,cl62,cl63,cl64,cl65,cl66,cl67,cl68,cl69,cl70,cl71,cl72,cl73,cl74,cl75,
cl76,cl77,cl78,cl79,cl80,cl81,cl82,cl83,cl84,cl85,cl86,cl87,cl88,cl89,cl90,
cl91,cl92,cl93,cl94,cl95,cl96,cl97,cl98,cl99,cl100,cl101,cl102,cl103,cl104,
cl105,cl106,cl107,cl108,cl109,cl110,cl111,cl112,cl113,cl114,cl115,cl116,
cl117,cl118,cl119,cl120,cl121,cl122,cl123,cl124,cl125,cl126,cl127,cl128,
cl129,cl130,cl131,cl132,cl133,cl134,cl135,cl136,cl137,cl138,cl139,cl140,
cl141,cl142,cl143,cl144,cl145,cl146,cl147,cl148,cl149,cl150,cl151,cl152,
cl153,cl154,cl155,cl156,cl157,cl158,cl159,cl160,cl161,cl162,cl163,cl164,
cl165,cl166,cl167,cl168,cl169,cl170,cl171,cl172,cl173,cl174,cl175,cl176,
cl177,cl178,cl179,cl180,cl181,cl182,cl183,cl184,cl185,cl186,cl187,cl188,
cl189,cl190,cl191,cl192,cl193,cl194,cl195,cl196,cl197,cl198,cl199,cl200,
cl201,cl202,cl203,cl204,cl205,cl206,cl207,cl208,cl209,cl210,cl211,cl212,
cl213,cl214,cl215,cl216,cl217,cl218,cl219,cl220,cl221,cl222,cl223,cl224,
cl225,cl226,cl227,cl228,cl229,cl230,cl231,cl232,cl233,cl234,cl235,cl236,
cl237,cl238,cl239,cl240,cl241,cl242,cl243,cl244,cl245,cl246,cl247,cl248,
cl249,cl250,cl251,cl252,cl253,cl254,cl255,cl256,cl257,cl258};

int
main(int argc,char** argv)
{
COLOR color = cl258;
color = cl2;
return 0;
}
PUBLIC _main
_TEXT SEGMENT
_color$ = -4
_main PROC NEAR
push ebp
mov ebp, esp
push ecx

; 枚举类型最终是以无符号整数实现的
; COLOR color = cl258;
mov DWORD PTR _color$[ebp], 258

; color = cl2;
mov DWORD PTR _color$[ebp], 2

xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

类的构造函数

class MyClass
{
public:
     int i;
     int j;
     int k;
     MyClass();
     ~MyClass();
     void f(int i);
};
MyClass::MyClass(){
this->i=7;
this->j=9;
this->k=11;
}
MyClass::~MyClass(){}
void MyClass::f(int j)
{
this->j = j;
__asm{
     mov this,5
}
}

int
main(int argc,char** argv)
{
MyClass *my = new MyClass();
my->f(3);
return 0;
}
;-----------------------------------------------
; MyClass::MyClass
;-----------------------------------------------
PUBLIC        ; MyClass::MyClass
_TEXT SEGMENT
_this$ = -4
PROC NEAR       ; MyClass::MyClass

push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 7
mov ecx, DWORD PTR _this$[ebp]
mov DWORD PTR [ecx+4], 9
mov edx, DWORD PTR _this$[ebp]
mov DWORD PTR [edx+8], 11      ; 0000000bH
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
ENDP        ; MyClass::MyClass
_TEXT ENDS


;-----------------------------------------------
; MyClass::~MyClass
;-----------------------------------------------
PUBLIC        ; MyClass::~MyClass
_TEXT SEGMENT
_this$ = -4
PROC NEAR       ; MyClass::~MyClass
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov esp, ebp
pop ebp
ret 0
ENDP        ; MyClass::~MyClass
_TEXT ENDS


;-----------------------------------------------
; MyClass::f()
;-----------------------------------------------
PUBLIC        ; MyClass::f
_TEXT SEGMENT
_j$ = 8
_this$ = -4
PROC NEAR       ; MyClass::f
push ebp
mov ebp, esp
push ecx
push ebx
push esi
push edi
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _j$[ebp]
mov DWORD PTR [eax+4], ecx
mov DWORD PTR _this$[ebp], 5
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 4
ENDP       ; MyClass::f
_TEXT ENDS


;-----------------------------------------------
; 主函数
;-----------------------------------------------
PUBLIC _main

;new操作符号就是一个函数
EXTRN        ; operator new
_TEXT SEGMENT
_my$ = -4
$T254 = -8
_main PROC NEAR
; Line 27
push ebp
mov ebp, esp
sub esp, 12
; MyClass *my = new MyClass();

; 调用new()在堆中分配一块内存,大小为MyClass左右成员变量大小之和,this指针在计算之内
; 12入栈,12是new的参数,由于编译时期MyClass已知,所以掉用new的时候不用给出参数,由编译器自己计算
push 12
call        ; operator new
add esp, 4

; new()将在堆中分配的内存块的首地址放在EAX,然后将EAX中的内容(首地址)放入变量my中
mov DWORD PTR $T254[ebp], eax

; 检查new()是否分配失败,如果失败则跳转到$L255
cmp DWORD PTR $T254[ebp], 0
je SHORT $L255

; 如果分配成功,以new()返回的首地址为参数调用MyClass的构造函数
mov ecx, DWORD PTR $T254[ebp]
call       ; MyClass::MyClass
mov DWORD PTR -12+[ebp], eax
jmp SHORT $L256
mov DWORD PTR -12+[ebp], 0
mov eax, DWORD PTR -12+[ebp]
mov DWORD PTR _my$[ebp], eax
; my->f(3);
push 3
mov ecx, DWORD PTR _my$[ebp]
call       ; MyClass::f


xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP

指针和地址

int main(int argc, char *argv[])
{
int i=7;
char c = 'c';
int *pi=&i; 
char *pc=&c;
pi = (int*)pc;

return 0;
}
PUBLIC _main
_TEXT SEGMENT
_i$ = -12
_c$ = -8
_pi$ = -4
_pc$ = -16
_main PROC NEAR

push ebp
mov ebp, esp
sub esp, 16

; int i = 7;
mov DWORD PTR _i$[ebp], 7

; char c = "c";
mov BYTE PTR _c$[ebp], 99

; int *pi = &i;
lea eax, DWORD PTR _i$[ebp]
mov DWORD PTR _pi$[ebp], eax

; char *pc = &c;
lea ecx, DWORD PTR _c$[ebp]
mov DWORD PTR _pc$[ebp], ecx

; pi = (int*)pc; 
mov edx, DWORD PTR _pc$[ebp]
mov DWORD PTR _pi$[ebp], edx

xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

更新:
推荐下面这本书,对理解c++如何实现各种语言特性很有帮助

[C++反汇编与逆向分析技术揭秘](https://book.douban.com/subject/6849800/)

我的小程序NuTs IPScanner

扫描局域网内所有主机(禁止ping的主机无法显示)起初是为了查当前网段可用的IP,增加手动设置IP的成功率,之后有了 NuTs IWantIP就基本不用了不过用来查主机与IP映射还是很快的程序采用多线程,速度很快~

NuTs Login&Out

科大专用上网登录程序学校校园网实行实名制,每个同学上网都要到特定网页输入学号和密码,这本来挺好的,可问题是,当用户关机的时候如果不注销帐号,那么当下次开机,IP改变后,无法访问任何网页,提示说帐号被上一个IP占用,而上一个IP如果恰好又被别人使用,那么那个人就可以使用你的帐号上网. 而你只有哭的份了 (幸好学校上网不收费)于是我写了这个登录/注销程序. 程序会在关机/重启/注销时自动注销上网帐号,这样就可以避免上述问题.程序还可以定时检测网络连通性(ping baidu.com)并刷新arp缓存. 这样可以解决arp欺骗导致的无法上网的问题.(宿舍楼的arp欺骗很严重~)这个程序还是很好用滴,不过在离开科大后就再也用不上了.科大的同学可以在 http://city.ibeike.com 的编程板块(还是软件,我忘了)找到下载

C++函数调用中的引用和指针

C++初学者对函数调用中的引用总是感到十分迷惑

下面就研究一下

先看下面三个函数

void f1(int a,intb) //按值传递
{
     a=0;
     b=3;
}

void f2(int &a,int &b) //按引用传递
{
     a=0;
     b=3;
}

void f3(int *a,int *b) //按指针传递
{
     *a=0;
     *b=3;
}

要想知道他们之间的区别,最好的方法是看看编译器究竟干了什么
用cl.exe 的-Fa参数,生成对应源代码的汇编代码,摘要如下

按值传递

PROC NEAR 
push ebp
mov ebp, esp
;直接将压入栈的2个参数赋值为0和3
mov DWORD PTR _a$[ebp], 0
mov DWORD PTR _b$[ebp], 3
pop ebp
ret 0

按引用传递

PROC NEAR

push ebp
mov ebp, esp
;将压入栈的参数作为指针,将0和3赋值给指针所指向的内存单元
mov eax, DWORD PTR _a$[ebp]
mov DWORD PTR [eax], 0
mov ecx, DWORD PTR _b$[ebp]
mov DWORD PTR [ecx], 3
pop ebp
ret 0

按指针传递

PROC NEAR

push ebp
mov ebp, esp
;可以看到,按引用传递和按指针传递的汇编代码一模一样
;但是使用引用,我们不必再用星号解引用了,这样提高了代码的可读性
;语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,
;只不过编译器帮我们完成了转换
mov eax, DWORD PTR _a$[ebp]
mov DWORD PTR [eax], 0
mov ecx, DWORD PTR _b$[ebp]
mov DWORD PTR [ecx], 3
pop ebp
ret 0

引用既具有指针的效率,又具有变量使用的方便性和直观性

C++、Java、Delphi类继承的对比

总体来说

  • c++: 完全的控制,极大的灵活性,灵活带来的复杂
  • java:比c++"保守",虚拟机风格的体系结构
  • delphi:很保守

inclusion/using clause

#include <iostream>
using namespace std;
import java.io.*;
uses windows,system,sysutils;

inheritance
class Child:[public | protected | private] Fahter,public Mother{
            ...
}

C++是这三种语言中继承最为复杂的.
分为 public,protected,private继承,同时支持真正的多继承

public继承(又称类型继承)和java的继承一样,保持父类的访问控制

protected继承将父类public成员改变为protected的

private继承(又称实现继承)不直接支持父类的公有接口,而提供自己的公有接口

所以private继承中,子类将父类中原先的protected成员和public成员都置为private的
所以原则上private继承中的子类应该是继承树中的叶节点,它只有private和public成员而
没有protected成员.它不应该再被继承,类似于Java中的final class

class Child extends Father implements IInterface{
            ...
}
TChild = class(TFather,IInterfacedObject)
            ...
end;

java 和 delphi 的继承相似,都是单继承同时可以实现多个接口

Access Control/Organization

class Foo{
   private:
    int i;
             int j;
             void f();
   protected: 
             int k;
   public: 
             int o;
}

class Foo /* extends Object */ {
  private
             int i;
             int j;
             void f(){...};
   protected
             int k;
   public
             int o;
}
TFoo = class(TObject)
   private 
             i:Integer;
             j:Integer;
   protected
             k:Integer;
   public
             o:Integer;
   publicshed
   property
             Tag:Integer read i write i;
end;

如果不给出访问控制,
c++默认是private
java默认是包内可见的
delphi默认是public的(单元内可见?)

c++的访问控制和代码管理最为丰富:
可以通过#include包含c/cpp头文件,提供向c的兼容
同时可以使用名字空间防止名字冲突,并且像c一样将代码分为头文件和实现文件
同时丰富性也带来了复杂性.使得c++的文件组织没有java和delphi清晰

java提供了松散的代码组织方式–包(package),而delphi提供了单元的代码组织方法
这两种组织方法相似,都是给了我们一个存放一组相关类的地方.而这个地方,在java中
是一个文件系统上的文件夹,在delphi中是一个单元文件
相比之下,我还是喜欢Delphi的组织方法:当类很多的时候java包中的文件变得既多又不好管理

虚函数 vs 动态函数

C++中有虚函数。而Delphi中既有虚函数还有动态函数。二者在语义上是等价的。

Virtual and dynamic methods are semantically equivalent. They differ only in the implementation of method-call dispatching at runtime.

区别在于虚函数优化了速度,而动态函数优化了代码大小

Virtual methods optimize for speed, while dynamic methods optimize for code size.

In general, virtual methods are the most efficient way to implement polymorphic behavior.

总体上来讲,虚函数是实现多态行为的最有效的方法

Dynamic methods are useful when a base class declares many overridable
methods which are inherited by many descendant classes in an application, but only
occasionally overridden.

当基类声明了很多可重写但是很少重写的方法时使用动态函数,
这样可一减小代码大小。在设计Framework时常使用这个技术

MFC学习笔记#1

作为Windows的Framework需要解决的一个问题就是
消息映射 Message Mapping

Delphi中由于有编译器的支持,可以直接在用户编写的类中处理需要处理的消息:

procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;

在MFC中就比较复杂了. afx使用宏来实现Windows消息到处理函数的映射而没有使用会带
而外的开销:由于C++中实现虚函数需要一个分发表vtable,而无论子类是否重写父类的虚函数,
都需要4个字节的开销.在CWnd类中至少要处理100个以上的消息.这样设计会带来很大的问题.

afx用一组宏将用于消息映射的数据保存在类中.大概情况是:记录该类可以处理的Windows消息,
当消息来到时,对比自己可以处理的消息列表,如果可以处理,就交给列表中记录的函数来处理.
而这个函数就是我们自己些的.比如 OnLButtonDown等

afx.h 中声明了mfc中最基础的类

struct CRuntimeClass;                  // object type information

class CObject;                                // the root of all objects classes

class CException;                         // the root of all exceptions
          class CArchiveException;              // archive exception
          class CFileException;                 // file exception
          class CSimpleException;
           class CMemoryException;               // out-of-memory exception
           class CNotSupportedException; // feature not supported exception

class CFile;                              // raw binary file
          class CStdioFile;                     // buffered stdio text/binary file
          class CMemFile;                       // memory based file

// Non CObject classes
class CString;                                // growable string type
class CTimeSpan;                              // time/date difference
class CTime;                                  // absolute time/date
struct CFileStatus;                           // file status information
struct CMemoryState;                          // diagnostic memory support

class CArchive;                               // object persistence tool
class CDumpContext;                           // object diagnostic dumping

先看一下CRuntimeClass

struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;//保存类名
int m_nObjectSize;//类对象大小
UINT m_wSchema; // schema number of the loaded class//是否可序列化
CObject* (PASCAL* m_pfnCreateObject)(); // 指向类的构造函数的指针

           //指向父类CRuntimeClass的指针
#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif

// Operations
CObject* CreateObject(); //动态创建
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; //类型鉴别

// Implementation
void Store(CArchive& ar) const; //序列化
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum) ;//"反序列化"

// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass;               // linked list of registered classes //注册类列表
};

CRuntimeClass 是MFC中很重要的一个类,动态创建实例是最常用的功能之一

// Helper macros for declaring CRuntimeClass compatible classes

#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) 
protected: 
static CRuntimeClass* PASCAL _GetBaseClass(); 
public: 
static const AFX_DATA CRuntimeClass class##class_name; 
virtual CRuntimeClass* GetRuntimeClass() const; 

#define _DECLARE_DYNAMIC(class_name) 
protected: 
static CRuntimeClass* PASCAL _GetBaseClass(); 
public: 
static AFX_DATA CRuntimeClass class##class_name; 
virtual CRuntimeClass* GetRuntimeClass() const; 

#else
#define DECLARE_DYNAMIC(class_name) 
public: 
static const AFX_DATA CRuntimeClass class##class_name; 
virtual CRuntimeClass* GetRuntimeClass() const; 

#define _DECLARE_DYNAMIC(class_name) 
public: 
static AFX_DATA CRuntimeClass class##class_name; 
virtual CRuntimeClass* GetRuntimeClass() const; 

#endif

// not serializable, but dynamically constructable
#define DECLARE_DYNCREATE(class_name) 
DECLARE_DYNAMIC(class_name) 
static CObject* PASCAL CreateObject();

#define _DECLARE_DYNCREATE(class_name) 
_DECLARE_DYNAMIC(class_name) 
static CObject* PASCAL CreateObject();

#define DECLARE_SERIAL(class_name) 
_DECLARE_DYNCREATE(class_name) 
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);

CObject
基本服务:

BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const; //类型鉴别

// Overridables
virtual void Serialize(CArchive& ar); //序列化

// Diagnostic Support //诊断支持
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;

//动态创建,利用CRuntimeClass里的m_pfnCreateObject
CObject* CRuntimeClass::CreateObject()

CWnd 在afxwin.h中定义,在wincore.cpp中实现
CWnd封装的功能

// Window tree access
// Message Functions
// Message processing for modeless dialog-like windows
// Window Text Functions
// CMenu Functions - non-Child windows only
// Window Size and Position Functions
// Coordinate Mapping Functions
// Update/Painting Functions
// Timer Functions
// ToolTip Functions
// Window State Functions
// Dialog-Box Item Functions
// Scrolling Functions
// Window Access Functions
// Alert Functions
// Clipboard Functions
// Caret Functions
// Shell Interaction Functions
// Icon Functions
// Context Help Functions
// Dialog Data support
// Help Command Handlers
// Layout and other functions
// OLE control wrapper functions

/* 消息处理函数 */
// Window-Management message handler member functions
// Nonclient-Area message handler member functions
// System message handler member functions
// Input message handler member functions
// Initialization message handler member functions
// Clipboard message handler member functions
// Control message handler member functions
// MDI message handler member functions
// Menu loop notification messages

Windows 控件的实现文件在 winctrl1.cpp~winctrl7.cpp

Posts navigation

1 2 3 4 5 6
Scroll to top