首先我们看一个“复杂”的Win32汇编程序
程序用来显示一个消息框
--------------------------------------------------
文件名:3.asm
.386
.model flat ,stdcall
NULLequ 0
MB_OK equ 0
ExitProcess PROTO :Dword
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:Dword
includelib kernel32.lib
includelib user32.lib
.data
szTextdb "Hello, world!",0
szCaption db "Win32Asm",0
.code
start:
push MB_OK
lea eax,szCaption
push eax
lea eax,szText
push eax
push NULL
call messageboxa
xor eax,eax
push eax
call exitprocess
end start
--------------------------------------------------
编译链接:
分下面两步进行:
ml /c /coff 3.asm
link /subsystem:Windows /libpath:d:\masm7\lib 3.obj
第一步编译生成3.obj文件
/c 表示只编译,不链接
/coff 表示生成COFF格式的目标文件
第二步链接生成3.exe文件
/subsystem:windows 表示生成Windows文件
/libpath:d:\masm7\lib 表示引入库的路径为:d:\masm7\lib。
在安装Masm32后,引入库位于Masm32\Lib目录下。
也可设置环境变量Lib的值:在dos提示符下键入Set Lib=d:\masm7\lib,这样“链接”就可简单写成:
link /subsystem:Windows 3.obj,试想一下,在程序调试过程中,修改源程序是常用的事啦,每次编译链接都要带/libpath:...那该有多烦人呢。当然,我们也可在源程序中直接给出引入库的位置,这样,链接时就方便啦,如下:
includelib d:\masm7\lib\kernel32.lib
includelib d:\masm7\lib\user32.lib
--------------------------------------------------
执行:在dos提示符下键入3,回车,出现一个消息框,哈哈,真正的Win32程序!
--------------------------------------------------
深入分析:
看一下源程序,有这么两行:call messageboxa\call exitprocess。大家一看都知道,这是子程序调用,但是我们并没写这样的子程序,事实上,这些是API函数。作为函数,我们在调用时可能需要传送给函数一些参数,程序怎么知道传送的参数有哪些,类型是什么呢?就是通过函数原型定义,如下所示:
ExitProcess PROTO :Dword
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:Dword
可以看出,ExitProcess有一个参数,MessageBoxA有四个参数,这些参数都是Dword类型。
在Win32中,参数的传递都是通过堆栈来完成的。象MessageBoxA这个函数有四个参数,究竟是左边的先压入堆栈还是右边的先入栈呢?.model flat,stdcall给出了答案。stdcall 指定参数是从右到左压入堆栈的,且调整堆栈是在子程序返回时完成的。在源程序中不需要用“add sp,值”来保持堆栈平衡。对MessageBox,在API手册中是这样定义的:
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType&n
您正在看的汇编语言是:hello,world!win32汇编小程序。
bsp// style of message box
)
所以会有我们的程序段:
push MB_OK
lea eax,szCaption
push eax
lea eax,szText
push eax
push NULL
call messageboxa
看看上面的程序,不难想到,假如在写程序时,少往堆栈里压入一个数据,那将是一个致命的错误。能不能将这种检查参数个数是否匹配的工作交给计算机来完成呢?这是可以的,INVOKE指令可以帮助我们完成这样的工作。假如你的参数个数不正确,连接器将给出错误提示。所以,极力建议你使用invoke代替call来调用子程序,当然,这不是绝对的。使用invoke上面的指令就可简写成下面的样子,看起来简炼多啦,查错也方便啦!
invoke messageboxa, NULL,addr szText,addr szCaption,MB_OK
另外,像NULL,MB_OK都是一些常量,这样的常量有很多,还有很多的结构,如果在我们的程序中一开始都写这么多的东西,可能一下子就把你吓怕啦,也容易出错,更不便于看程序的主要部分。hutch整理的Windows.inc包含了WIN32编程所需要的常量和结构体的定义,我们可简单的用一个include指令将这些常量和结构的定义插入到我们的文件中:
include d:\masm32\include\Windows.inc
但是Windows.inc中并不包含函数原型的声明,还要从其他的头文件中得到函数原型的声明,比如:messageboxa的原型声明在user32.inc文件中,exitprocess在kernel32.inc文件中。这些头文件都放在 \masm32\include文件夹下。
还有,要用Windows.inc,必须使用option casemap:none,它的意思是告诉 MASM 要区分符号的大小写,譬如:start和START是不一样的。否则,一个小小的程序,可能会出成百上千的错误呀!
其他的,就不再细说啦,到此,上面的程序可重新修改如下:
-----------------------------------------------------------------
最终的结果
.386表示要用到386指令
.model flat,stdcall 32位程序,要用flat啦!stadcall,标准调用
option casemap:none 区别大小写
includeWindows.inc 包括常量及结构定义
includekernel32.inc 函数原型声明
includeuser32.inc
includelib kernel32.lib 用到的引入库
includelib user32.lib
.data数据区,定义2个字符串
szTextdb "Hello, world!",0
szCaption db "Win32Asm",0
.code 代码开始执行处
start:
invoke MessageBox,NULL,addr szText,addr szCaption,MB_OK
调用MessageBoxAPI函数
invoke ExitProcess,NULL 程序退出
end start结束
------------------------------------
编译链接:
ml /c /coff /I d:\masm7\include 3.asm 注意开关符识别大小写
link /subsystem:Windows /libpath:d:\masm7\lib 3.obj
/I d:\masm7\include 表示*.inc文件的位置,也可设置环境变量Set include=d:\masm7\include来简化 *** 作,也可在程序中明确指出*.inc的位置。
前面讲的都是用两条指令来完成编译链接,实际上用一条指令也可完成,如下:
ml /coff /I d:\masm7\include 3.asm /link /subsystem:Windows /libpath:lib
若*.inc及引入库在源程序中都明确指出其位置,则可简化为:
ml /coff 3.asm /link /subsystem:
您好!虽然有点多,但对您总是有好处的,看得越多,好处也越多。呵呵
C++语言相对于C语言来说引入了两个大的新东西,一个就是面向对象(具体来将就是类
),另外就是模板技术(模板编程或者叫泛型编程是进来非常流行的技术,在C#中虽然还
没有对泛型编程的支持,但是相信在将来也一定会加入这一功能),模板是C++中比较复
杂的部分,但是作为一个真正的C++程序员,这部分很重要。尤其是对C++标准程序库
的掌握尤为重要。
刚开始的时候千万不要直扑VC中的各种向导和设计器。因为依赖开发环境生成的很多代码
会把我们搞糊涂,也不利于我们学习C++语言本身。我的建议就是生成一个空的控制台工
程,然后自己向里面添加文件。
如下就是一个简单的控制台程序:
//robindy/list.cpp
#i nclude
#i nclude
using namespace std
int main()
{
list coll
for(char c = 'a'c <= 'z'++c)
{
coll.push_back(c)
}
list::const_iterator pos
for(pos = coll.begin()pos != coll.end()++pos)
{
cout <<*pos <<' '
}
cout <<endl
return 0
}
对using namespace std的解释:
所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:
std::cout <<std::hex <<3.4 <<std::endl
2、使用using关键字。
using std::cout
using std::endl
以上程序可以写成
cout <<std::hex <<3.4 <<endl
3、最方便的就是使用using namespace std这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:
cout <<hex <<3.4 <<endl
这段程序用到了标准模板库,在屏幕上打印字符a~z,其中main函数的标记式只有两种是被
C++标准委员会接受的,只有以下两种写法是符合C++标准的,是可移植的。即:
int main()
{
}
和
int main(int argc, char* argv[])
{
}
C++在main()的末尾定义了一个隐式的return 0但是在VC中必须显式的写出return语句。
在BCB中可以不写return而编译通过。
这段程序中用到了STL中的容器:链表。先是向链表中插入26个字母,然后从遍历链表,输
出字符。
我不建议初学者一开始就从模板库入手,但是我建议应该逐渐地有意识地学习模板库。如
我们应该熟悉cout和cin的用法(位于iostream),而减少使用老式的C函数库中printf和sc
anf等。
从控制台入手的好处就是避免我们理解VC中向导等工具生成的其他代码,整个程序的流程
很清楚。新手学习VC的一大难点就是搞不清楚整个程序的流程(从那条语句开始执行,然后
从那条语句退出。MFC做了太多封装,掩盖了事实的真相,^_^)。通过控制台我们可以很快
地学习C++语言中的很多新特性。如类的封装、继承和多态等。通过这个时期的学习,要
能够掌握三个东西: C++关键字、语法(重点是和面向对象有关的,以及模板)、C++
标准程序库(知道怎么使用即可,要彻底掌握需要很多时间和精力)。至于与界面有关的
东东(MFC中很大一部分是与界面相关的类,所以我认为MFC很臃肿而无聊!回头看过以前
用MFC写的程序,有一种“垃圾”的感觉。)
自己做个测试:
写一个控制台程序,在其中体现出类的继承,函数重载,动态多态(通过虚函数实现),
数据封装,C++标准库的运用。
如果你能够轻松搞定,恭喜你,你已经通过C++语言关了。不过C++实在是一个复杂的
东东,其中有各种千奇百怪的语法现象,如果没有三、五年的功力,千万不要说自己懂C+
+。^_^。
推荐书籍《Thinking in C++》(有精力的话,可以直接读原版)
《Essential C++》
本来我计划继续说说如何来进入C++世界的,但是琢磨了一下,觉得还是要先解决一个问
题:为什么要学习C++?我觉得在软件开发这个行当了里除了为了生计外,学习新东西都
是应该以兴趣为导向的。所以撇开我个人对C++的偏爱,我想为你树立起学习C++的信
心和兴趣。当初我对自己学习C++的第一个忠告就是:不能半途而废!起初是兴趣驱动,
后来则是生计所需,最后还是回归到了兴趣。毕竟我从中得到了乐趣,这就足够了。在学
一个新东西前,解决动机问题很重要。就如同杀人一样,如果只是突然兴起,那么等尝试
了以后,是没有成就感的。革命先烈们为我们作出了很好的榜样,就算我们在学习C++的
道路上遇到了太多困惑和痛苦,但是我们毕竟为自己的信念做了努力,我们知道自己在
做什么,知道自己在追求什么。
C++适合做什么样的开发?
C++是一门广泛用于工业软件研发的大型语言。具有很高的复杂性和解决问题的能力。C
++不仅在开发上极具价值,同时在学术界也就有很高的价值。有关C++的文章应该可以
用浩如烟海来形容了吧。C++的世界级经典书籍也是数不胜数。然而,目前开发语言是如
此地繁荣,就连微软也在推出了新的开发语言C#。一个不可否认的现实是,在低阶程序设
计领域,C++挤压着C同时也在承受着C的强烈反d,前段时间看了据说是微软 *** 作系统源代
码的东东,其中很多还是C语言。而在高阶程序设计领域,Java和C#正在不断蚕食着C++的
地盘。也许Java和C#的狂潮终将迫使C++回归本位— 回到它有着根本性优势的开发领域:
低级系统程序设计、高级大规模高性能应用设计、嵌入式程序设计、通用程序设计以及数
值科学计算等。果真如此,我认为这未尝不是一件好事。电力系统软件所要求的高性能和
大规模数值计算正是C++所擅长的。就我所接触的南瑞和鲁能,很多涉及到电力系统计算
的软件如PAS等,都是用C++来开发的。在电力系统软件开发这块阵地,C++大有用武之
地。C++吸引如此之多的智力投入,以至于这个领域的优秀作品,包括重量级的软件产品、
程序库以及书籍等,数不胜数。在C++之父Bjarne Stroustrup的个人主页上,有一页
列出了一些(全部或大部分)使用C++编写的系统、应用程序和库。
下面是一些例子(摘自荣耀网站):
o Adobe Systems:所有主要应用程序都使用C++开发而成,比如Photoshop &ImageReady
、Illustrator和Acrobat等。
o Maya:知道“蜘蛛人”、“指环王”的电脑特技是使用什么软件做出来的吗?没错,就
是Maya。
o Amazon.com:使用C++开发大型电子商务软件。
o Apple:部分重要“零件”采用C++编写而成。
o AT&T:美国最大的电讯技术提供商,主要产品采用C++开发。
o Google:Web搜索引擎采用C++编写。
o IBM:OS/400。
o Microsoft:以下产品主要采用C++(Visual C++)编写:
o Windows XP Windows NT:NT4、2000 Windows 9x:95、98、Me Microsoft Office:Wo
rd、Excel、Access、PowerPoint、Outlook Internet Explorer,包括Outlook Express
Visual Studio:Visual C++、Visual Basic、Visual FoxPro .NET Framework类库采用C
#编写,但C#编译器自身则使用C++编写而成。Exchange SQL Server FrontPage Project
所有游戏......
o KDE:K Desktop Environment(Linux)。
o Symbian OS:最流行的蜂窝电话OS之一。
C++源于C语言,还记得很久以前学习C语言的时光(那是一段快乐而充实的时光),可是
现在学习C++,并不是在C的基础上加上了类而已,如果这样认为,我们是耍不好C++的
。因此,C++绝不是C的升级或扩充,我们应该把C++当作一门新语言来学习(C++之
父Bjarne Stroustrup语)。
写程序首先希望是程序能正确执行,其次是效率能够被接受,再次就是易于维护。C++是
一个难学易用的语言。C++提供了太多可选择的东西,而且使用使用C++来写程序可以
有四种思考模式:基于过程、基于对象、面向对象和泛型。我们使用一种语言来写程序,
并不意味着就是使用语言本身,换句话说,我们更多的时候是使用程序库在写程序。比如
MFC、STL、ATL、VCL等等。其中要使用C++来写出结构优美、性能卓越、代码简洁、易于
维护的代码,首推C++标准程序库。STL对效率做了严格的要求,而且使用STL写出来的程
序简洁美观(前段时间我特意贴了一个要求对若干整数进行排序的帖子,其实目的就是用来
展示STL的简洁优雅)。一旦习惯使用泛型思维来考虑问题,我们能够充分体会到模板带来的美!
对于数值计算来说,C++标准程序库可以充分满足现代化服务和商业计算对数据、信息的即
时回应的要求。
我觉得学好一门语言最重要的就是实践。也就是多“写”!“工程经验之积累”对已具有
一段开发时间的程序员而言,非常重要!只有在不断的积累中,我们才能渐渐体会到C++
语言中的一些背后的东西。对于这点,没有大量程序代码写作经验的菜鸟,也可以借助《
Effective C++》先攒一些经验值。《Effective C++》是一本好书!。Meyers的书绝对值
得一读,Meyers可以说当今C++社群中数一数二的技术专家。
推荐网站:
www.royaloo.com
以下文字应该是去年所涂鸦而成,主要是关于动态内存分配的,在这里将其重新看了看
,觉得还是写得太浅薄了。因为内存是程序运行的“运动场”,对场地的了解程度会直接
影响到我们程序运行的流畅度和稳定性。
C++提供了 *** 作符new来在堆上分配内存, *** 作符delete来释放内存。有些情况下,我
们需要对内存的分配和释放进行更好的控制。许多程序创建和释放一些重要类的大量的对
象,如tree nodes,linked lists links,points,lines,messages,etc.使用通用的内存分
配器如new和delete来进行这些对象的分配和释放有时将支配程序的运行时间和内存需求。
两方面的因素:通用内存分配 *** 作的运行和空间的耗费以及不同对象大小引起的内存碎片
。类使用定制的内存分配器将加快模拟器、编译器和类似程序的执行速度。
例外一种需要更好的内存控制的情况是:需要在有限资源的情况下长时间不间断运行
的程序。实时系统经常需要用最少的耗费来获取有保证的可预期的内存。这也就导致了更
好的内存控制的需要。一般来说,这些程序都避免使用动态的内存分配,而使用特殊目的
的内存分配器来管理有限资源。
此外,还有一些情况下由于硬件或系统的要求,需要将对象放在指定的内存位置。这也
需要进行定制的内存管理(通过重载new来加以实现)。
在C++ Release 2.0中,为了满足以上需求,内存管理机制做了相应的修改。主要是引
进了operator new [] 和 operator delete []。
new *** 作符的作用范围(Scope for operator new Functions)
*** 作符(Operator) 范围(Scope)
::operator new Global
class-name::operator new Class
operator new的第一个参数必须是类型size_t(在STDDEF.H中定义的类型),返回类型
为void *。
当分配内建(built-in)类型的对象、未包含用户自定义的new *** 作符函数的类对象、任何
类型的数组时,使用全局new *** 作符函数。当在类中自定义new *** 作符时,分配该类对象的
内存时,调用该类的new *** 作符。如下:
#i nclude
#i nclude
class Blanks
{
public:
Blanks(){}
void *operator new( size_t stAllocateBlock, char chInit )
}
void *Blanks::operator new( size_t stAllocateBlock, char chInit )
{
void *pvTemp = malloc( stAllocateBlock )
if( pvTemp != 0 )
memset( pvTemp, chInit, stAllocateBlock )
return pvTemp
}
int main()
{
Blanks *a5 = new( 0xa5 ) Blanks//创建对象Blanks,并且初试化为0xa5
return a5 != 0
}
new *** 作符可以重载,而delete却不行。因为等到需要释放的时候,我们所能得到的就
是一个指针。而且该指针可能不是原先的对象类型指针(有可能进行了类型转换)。实际
上,当使用new获得一个指向一片内存的指针时,在该片内存前有一个指示器(indicator)
,记录实际分配的内存数量。当调用delete时,可以获知需要释放的内存大小。
数组的释放(Deallocating Arrays):
void f( )
{
X* p1 = new X[10]
//...
delete [] X
}
为什么不使用delete [10] X来释放内存?Bjarne Stroustrup称这种做法容易导致错
误,而将记录元素个数的任务放在delete的实现中了。
至于为什么C++中未内建垃圾收集器(Garbage Collection)的原因,看《C++语言的设
计和演化》(En) Bjarne Stroustrup 机械工业出版社(俗称:D&E)可以得到答案。
此外,C++标准库中提供了一种智能型指针auto_ptr,这种指针可以帮助我们防止“被
异常抛出时发生资源泄漏”。但是缺点是该智能型指针不能指向数组,因为其内部释放内
存是通过delete而非delete [] 来进行的。所以,只能使用其来指向一个单个对象。
模板部分是C++中比较难的部分,也是C++的魅力所在。以下文字是我以前看过的,具
体出处不清楚了。今天稍微整理了一下,作为模板介绍的一个单元。
为什么要使用模板
对于除类型之外,其余都相同的函数(譬如quicksort),我们一般有3种解决办法。
1、针对每个不同的类型重复地编写函数实体(C语言的做法):
int* quicksort(int a[]) {... }
double* quicksort(double a[]) {... }
…
2、使用Object(Java的做法)或者void*
缺点有两个
效率问题方面也有问题
类型检查问题
3、使用宏预处理机制
缺点:只是愚蠢的文本替换,而且也不会考虑作用域和类型安全。
然而,应用模板却可以避免这些缺点,我们可以编写:
template
T* quicksort(T a[]) {... }
优点:
代码简洁优雅,所有参数类型都以T来代替,真正实现了类型无关性。
更好的类型安全性,所有的类型检查都是在编译期进行,而且避免使
用指针。
不存在继承,效率高。(1)没有虚函数;(2)所有的一切工作都是
在编译期完成,大大提高运行效率。
目的:告诉编译器如何做出最佳的选择,而且这种选择
全部是在编译期完成的。
模板的机制:特化 和 实参演绎
1、特化
基本模板:
template
class A { // (1)
void f(T1 a, T2 b)
}
局部特化(偏特化):
template class A { // (2)
void f(int a, T2 b)
}
或者
template>class A { // (3)
void f(T a, T b)
}
全局特化(显式特化):
template<>
class A {
void f(int a, int b)// (4)
}
使用示例:
A* p1//将使用(4) ——全局特化
A* p2//将使用(3) ——局部特化
A* p3//将使用(2) ——局部特化
A* p4//将由(1) ——基本模板——生成
//A
优点:
由:全局特化->局部特化->基本模板,这种特化顺序的选择与匹配(重载解析规则)是由编译器自动进行的,无需人工参与。
可以根据不同的情况(诸如类型不同,条件不同),给出不同的实现,从而获得更加灵活的针对性。
可以针对任何变化,改善了程序的扩展性。
2 实参演绎
T const&f(T const&a, T const&b)
{
return a + b//1处
}
int g = f(1,2)
实际上f(1,2)要匹配的函数是int const&f(int const&,int const&)
而这个函数又是怎么来的呢?
优点:
再也无需提供一对尖括号和里面的实参,诸如f(1,2),有了
实参演绎,我们就可以写成f(1,2)。
模板的应用
1、标准库(STL)——到处都是模板代码
标准库=算法+容器+迭代器
如list /
2、类型无关性(T)
3、trait和policy
(1)trait: 主要用到了许多typedef和特化,指定的是一种特性。
// traits/accumtraits3.hpp
template
lass AccumulationTraits
c template<>
class AccumulationTraits {
public:
typedef int AccT
static AccT const zero = 0
}
template<>
class AccumulationTraits {
public:
typedef int AccT
static AccT const zero = 0
}
template<>
class AccumulationTraits {
public:
typedef long AccT
static AccT const zero = 0
}
(2)policy:通常表现为某个函数,指定的是一种行为
class SumPolicy {
public:
template
static void accumulate (T1&total, T2 const &value) {
total += value
}
}
(3)trait和policy的用法:
template>
class Accum {
public:
typedef typename Traits::AccT AccT
static AccT accum (T const* beg, T const* end) {
AccT total = Traits::zero()
while (beg != end) {
Policy::accumulate(total, *beg)
++beg
}
return total
}
}
4、Metaprogramming
编译期计算、递归的思想
5、新形式的设计模板
(第三、第四、第五点以后再详细介绍)
《C++ Templates中文版》的具体介绍
第1部分介绍了模板的基本概念,以教程的风格来介绍这些基本概念。
第2部分阐述了模板的语言细节,可以作为一本基于模板的构造的参考手册。
第3部分介绍了C++模板所支持的基本设计技术,范围覆盖从微小的概念一直延伸到复杂的用法;一些技术在别的书籍都没有出现过。
第4部分基于前两部分,深入讨论了各种使用模板的普通应用程序。
关于开发一个小程序,要根据采用的开发方式,比如:1.就是自己开发自己做。这也是最省钱的方式,如果自己来开发的话,就可以省掉那些付给开发人员的钱。通过这种方式,如果开发个人版的小程序,不需要花费一分钱就可以拥有自己的小程序。而如果要开发一个具有支付功能的小程序商城,是企业版的话,就需要在进行微信认证,而只需要给腾讯支付300元的认证费用即可,前提是自己要懂技术、最好是有团队。
2.就是自己有一个开发部门或者开发人员。如果是这种情况的话,我们也是和第一种情况一样的,但是还需要支付员工的费用,这也要算在开发一个小程序的成本中,毕竟,员工是需要公司养着的。而这个成本的多少就要取决于你开给员工的工资多少了,一般一个能够独立开发小程序的员工基本工资不会低于一万,这还是在小城市的基本工资,如果是北上广,需要大概两万到五万之间。而如果你养的是一个开发部门的话,这个费用将会更高。
3.就是找一个具有开发微信小程序商城资质的开发公司。这一个方式是在自己没有开发能力的情况下,最方便也比较省钱的方式。因为我们不需要耗费那么多精力去自己开发,也不需要花费太多的钱去养一个开发人员或是开发部门,我们只需要支付开发一个小程序的市场价格(几千元)即可。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)