卓's profile成都,又是成都PhotosBlogListsMore ![]() | Help |
|
|
June 11 VS的正则表达式工具还不错今天要对一组文本数据做处理,去掉一些不必要的,自然就想到了使用正则表达式。首先是用UltraEdit来做,发现很不方便,支持的规则比较少。例如,查找两位数字,只能用[0-9][0-9],不能使用类似[0-9]^2的模式。而且,不能对查找的字串分组,例如这样的用法是不合法的{12}+。
要实现我的目的,用这样的语法,匹配模式会写的很啰嗦,决定换个工具试试。选了M$的VS,一查才发现,功能还蛮丰富的,以前干这活都用的EditPlus,用VS看来会更方便一点。抄录MSDN相关部分如下:
The list of all regular expressions that are valid in Find and Replace operations is longer than can be displayed in the Reference List. You can also insert any of the following regular expressions into a Find what string:
September 26 SOAWhy SOA?企业系统的架构师认为SOA能够帮助业务迅速和高效地响应变化的市场条件[1] . 服务导向的架构在宏观(服务)上,而不是在微观上(对象)提高了重用性。同时,服务导向的架构可以简化与传统系统的互连和使用。 在某种意义上说,服务导向的架构可以被认为是一种演化,而不是革命。它捕捉到了之前体系架构的许多最佳实践或实际应用。比如在通信系统中,近年来进展有限的解决方案多采用完全静态的绑定来与网络中的其他设备沟通,但若正式采用SOA方式,解决方案就更能妥善定位,进而突显定义明确且可高度跨平台操作接口的重要性。 有些人质疑服务导向的架构是不是1970年代模块化编程,1980年代的面向事件设计,1990年代的基于接口/构件设计的一种复兴?(1990s). 服务导向的架构提升了将用户从服务实现分开的目标。服务可以运行在不同的服务器上,并通过网络被访问。 这也大大增加了服务的重用. SOA的原则[2] 以下指导原则是开发,维护和使用SOA的基本原则[3] 下面是一些特定的体系架构原则
除此以外,在定义一个SOA实现时,还需要考虑以下因素:
面向服务的架构和Web服务协议面向服务的架构通常被定义为通过Web服务协议栈暴露的服务 . 与SOA相关的Web服务的标准主要有:
注意,一个系统要成为面向服务的系统并不需要这些协议,比如一些面向服务的系统可以通过CORBA实现。 参考文献
July 27 函数调用约定在C语言中,假设我们有这样的一个函数: int function(int a,int b) 调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级 语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没 有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。 也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者 和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。 栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中 第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作 被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修 改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变 成栈顶,栈顶指针随之修改。 函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得 数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈 恢复原装。 在参数传递中,有两个很重要的问题必须得到明确说明: 当参数个数多于一个时,按照什么顺序把参数压入堆栈 函数调用后,由谁来把堆栈恢复原装 在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有: stdcall cdecl fastcall thiscall naked call stdcall调用约定 stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机 程序设计语言,其语法严谨,使用的函数调用约定就是stdcall。在Microsoft C++系列 的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLB ACK。 stdcall调用约定声明的语法为(以前文的那个函数为例): int __stdcall function(int a,int b) stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈 3)函数 名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸 以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处 翻译成汇编语言将变成: push 2 第二个参数入栈 push 1 第一个参数入栈 call function 调用参数,注意此时自动把cs:eip入栈 而对于函数自身,则可以翻译为: push ebp 保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出 时恢复 mov ebp,esp 保存堆栈指针 mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向 a add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b mov esp,ebp 恢复esp pop ebp ret 8 而在编译时,这个函数的名字被翻译成_function@8 注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。其中 在函数开始处保留esp到ebp中,在函数结束恢复是编译器常用的方法。 从函数调用看,2和1依次被push进堆栈,而在函数中又通过相对于ebp(即刚进函数时的 堆栈指针)的偏移量存取参数。函数结束后,ret 8表示清理8个字节的堆栈,函数自己 恢复了堆栈。 cdecl调用约定 cdecl调用约定又称为C调用约定,是C语言缺省的调用约定,它的定义语法是: int function (int a ,int b) //不加修饰就是C调用约定 int __cdecl function(int a,int b)//明确指出C调用约定 在写本文时,出乎我的意料,发现cdecl调用约定的参数压栈顺序是和stdcall是一样的 ,参数首先由有向左压入堆栈。所不同的是,函数本身不清理堆栈,调用者负责清理堆 栈。由于这种变化,C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大 特色。对于前面的function函数,使用cdecl后的汇编码变成: 调用处 push 1 push 2 call function add esp,8 注意:这里调用者在恢复堆栈 被调用函数_function处 push ebp 保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出 时恢复 mov ebp,esp 保存堆栈指针 mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向 a add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b mov esp,ebp 恢复esp pop ebp ret 注意,这里没有修改堆栈 MSDN中说,该修饰自动在函数名前加前导的下划线,因此函数名在符号表中被记录为_f unction,但是我在编译时似乎没有看到这种变化。 由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用 不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据 第一个后者后续的明确的参数确定下来,就可以使用不定参数,例如对于CRT中的sprin tf函数,定义为: int sprintf(char* buffer,const char* format,...) 由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。 fastcall fastcall调用约定和stdcall类似,它意味着: 函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过 从右向左的顺序压栈 被调用函数清理堆栈 函数名修改规则同stdcall 其声明语法为:int fastcall function(int a,int b) thiscall thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成 员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理,th iscall意味着: 参数从右向左入栈 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针 在所有参数压栈后被压入堆栈。 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈 为了说明这个调用约定,定义如下类和使用代码: class A { public: int function1(int a,int b); int function2(int a,...); }; int A::function1 (int a,int b) { return a+b; } #include int A::function2(int a,...) { va_list ap; va_start(ap,a); int i; int result = 0; for(i = 0 i < a i ++) { result += va_arg(ap,int); } return result; } void callee() { A a; a.function1 (1,2); a.function2(3,1,2,3); } callee函数被翻译成汇编后就变成: //函数function1调用 0401C1D push 2 00401C1F push 1 00401C21 lea ecx,[ebp-8] 00401C24 call function1 注意,这里this没有被入栈 //函数function2调用 00401C29 push 3 00401C2B push 2 00401C2D push 1 00401C2F push 3 00401C31 lea eax,[ebp-8] 这里引入this指针 00401C34 push eax 00401C35 call function2 00401C3A add esp,14h 可见,对于参数个数固定情况下,它类似于stdcall,不定时则类似cdecl naked call 这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增 加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回 结果。这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为: __declspec(naked) int add(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret } 注意,这个函数没有显式的return返回值,返回通过修改eax寄存器实现,而且连退出函 数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成: mov eax,[ebp+8] add eax,[ebp+12] ret 8 注意这个修饰是和__stdcall及cdecl结合使用的,前面是它和cdecl结合使用的代码,对 于和stdcall结合的代码,则变成: __declspec(naked) int __stdcall function(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret 8 //注意后面的8 } 至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。 函数调用约定导致的常见问题 如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两 种常见的问题: 函数原型声明和函数体定义不一致 DLL导入函数时声明了不同的函数约定 以后者为例,假设我们在dll种声明了一种函数为: __declspec(dllexport) int func(int a,int b);//注意,这里没有stdcall,使用的是 cdecl 使用时代码为: typedef int (*WINAPI DLLFUNC)func(int a,int b); hLib = LoadLibrary(...); DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定 result = func(1,2);//导致错误 由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破 坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏了。 April 04 什么是词法分析和语法分析词法分析(Lexical analysis或Scanning)和词法分析程序(Lexical analyzer或Scanner) 语法分析(Syntax analysis或Parsing)和语法分析程序(Parser) 语义分析(Syntax analysis) Lex Yacc 源语言(Source language)和源程序(Source program) 目标语言(Object language or Target language)和目标程序(Object program or Target program) 中间语言(中间表示)(Intermediate language(representation))
文法(Grammars) 内聚和耦合内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling)是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计追求强内聚,弱耦合。 一、内聚强度 内聚按强度从低到高有以下几种类型: (1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。 (2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的修改也比较困难。 (3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次序执行,则称为过程内聚。 (5)通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为通信内聚。 (6)顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。 二、耦合强度 耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用;(2)一个模块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合。一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。 (4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构的变化将使相关的模块发生变化。 (5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合。模块间没有信息传递时,属于非直接耦合。 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,坚决避免使用内容耦合。 April 03 一个完整开发环境应该具有的几个系统
March 06 bin-search/*
* Perform a binary search. * * The code below is a bit sneaky. After a comparison fails, we * divide the work in half by moving either left or right. If lim * is odd, moving left simply involves halving lim: e.g., when lim * is 5 we look at item 2, so we change lim to 2 so that we will * look at items 0 & 1. If lim is even, the same applies. If lim * is odd, moving right again involes halving lim, this time moving * the base up one item past p: e.g., when lim is 5 we change base * to item 3 and make lim 2 so that we will look at items 3 and 4. * If lim is even, however, we have to shrink it by one before * halving: e.g., when lim is 4, we still looked at item 2, so we * have to make lim 3, then halve, obtaining 1, so that we will only * look at item 3. */ void * bsearch(key, base0, nmemb, size, compar) const void *key; const void *base0; size_t nmemb; size_t size; int (*compar)(const void *, const void *); { const char *base = base0; size_t lim; int cmp; const void *p; for (lim = nmemb; lim != 0; lim >>= 1) { p = base + (lim >> 1) * size; cmp = (*compar)(key, p); if (cmp == 0) return ((void *)p); if (cmp > 0) { /* key > p: move right */ base = (char *)p + size; lim--; } /* else move left */ } return (NULL); } December 01 据说是Google的面试题目在常数e里找出第一个10位的质数(find the first ten-digit prime number in the mathematical constant called 'e')。
Thinking about this. November 30 编程规范不知道是哪个公司的编程规范,有些规定不错,有些不太赞成,有些不理解含义,放到这里做个参考。
基本要求 1.1 程序结构清析,简单易懂,单个函数的程序行数不得超过100行。 1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序。 1.3 尽量使用标准库函数和公共函数。 1.4 不要随意定义全局变量,尽量使用局部变量。 1.5 使用括号以避免二义性。 2.可读性要求 2.1 可读性第一,效率第二。 2.2 保持注释与代码完全一致。 2.3 每个源程序文件,都有文件头说明,说明规格见规范。 2.4 每个函数,都有函数头说明,说明规格见规范。 2.5 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义。 2.7 常量定义(DEFINE)有相应说明。 2.8 处理过程的每个阶段都有相关注释说明。 2.9 在典型算法前都有注释。 2.10 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab为 6个字节。 2.11 循环、分支层次不要超过五层。 2.12 注释可以与语句在同一行,也可以在上行。 2.13 空行和空白字符也是一种特殊注释。 2.14 一目了然的语句不加注释。 2.15 注释的作用范围可以为:定义、引用、条件分支以及一段代码。 2.16 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3 。 3. 结构化要求 3.1 禁止出现两条等价的支路。 3.2 禁止GOTO语句。 3.3 用 IF 语句来强调只执行两组语句中的一组。禁止 ELSE GOTO 和 ELSE RETURN。 3.4 用 CASE 实现多路分支。 3.5 避免从循环引出多个出口。 3.6 函数只有一个出口。 3.7 不使用条件赋值语句。 3.8 避免不必要的分支。 3.9 不要轻易用条件分支去替换逻辑表达式。 4. 正确性与容错性要求 4.1 程序首先是正确,其次是优美 4.2 无法证明你的程序没有错误,因此在编写完一段程序后,应先回头检查。 4.3 改一个错误时可能产生新的错误,因此在修改前首先考虑对其它程序的影响。 4.4 所有变量在调用前必须被初始化。 4.5 对所有的用户输入,必须进行合法性检查。 4.6 不要比较浮点数的相等, 如: 10.0 * 0.1 == 1.0 , 不可靠 4.7 程序与环境或状态发生关系时,必须主动去处理发生的意外事件,如文件能否逻辑锁定、打印机是否联机等。 4.8 单元测试也是编程的一部份,提交联调测试的程序必须通过单元测试。 5. 可重用性要求 5.1 重复使用的完成相对独立功能的算法或代码应抽象为公共控件或类。 5.2 公共控件或类应考虑OO思想,减少外界联系,考虑独立性或封装性。 5.3 公共控件或类应建立使用模板。 附:C++ 编程规范,delphi作相应的参考 .1适用范围 本标准适用于利用Visul C++ ,Borland C++进行软件程序开发的人员.。 .2变量命名 命名必须具有一定的实际意义,形式为xAbcFgh,x由变量类型确定,Abc、Fgh表示连续意义字符串,如果连续意义字符串仅两个,可都大写.如OK. 具体例程: BOOL类型 bEnable; ch * char chText c * 类对象 cMain(对象实例) h * Handle(句柄) hWnd i * int n * 无符号整型 p * 指针 sz,str * 字符串 w WORD x,y 坐标 Char或者TCHAR类型 与Windows API有直接联系的用szAppName[10]形式否则用FileName[10]形式,单个字符也可用小写字母表示; Int类型 nCmdShow; LONG类型 lParam; UINT类型 uNotify; DWORD类型 dwStart; PSTR类型 pszTip; LPSTR类型 lpCmdLine LPTSTR类型 lpszClassName; LPVOID类型 lpReserved WPARAM类型 wParam, LPARAM类型 lParam HWND类型 hDlg; HDC类型 hDC; HINSTANCE类型 hInstance HANDLE类型 hInstance, HICON类型 hIcon; int iTmp float fTmp DWORD dw* String , AnsiString str * m_ 类成员变量 m_nVal, m_bFlag g_ 全局变量 g_nMsg, g_bFlag 局部变量中可采用如下几个通用变量:nTemp,nResult,I,J(一般用于循环变量)。 其他资源句柄同上 .3常量命名和宏定义 常量和宏定义必须具有一定的实际意义; 常量和宏定义在#include和函数定义之间; 常量和宏定义必须全部以大写字母来撰写,中间可根据意义的连续性用下划线连接,每一条定义的右侧必须有一简单的注释,说明其作用; 资源名字定义格式: 菜单:IDM_XX或者CM_XX 位图:IDB_XX 对话框:IDD_XX 字符串:IDS_XX DLGINIT:DIALOG_XX ICON:IDR_XX .4函数命名 函数原型说明包括引用外来函数及内部函数,外部引用必须在右侧注明函数来源: 模块名及文件名, 如是内部函数,只要注释其定义文件名; 第一个字母必须使用大写字母,要求用大小写字母组合规范函数命名,必要时可用下划线间隔,示例如下: void UpdateDB_Tfgd (TRACK_NAME); //Module Name :r01/sdw.c void PrintTrackData (TRACK_NAME); //Module Name :r04/tern.c void ImportantPoint (void); //Module Name :r01/sdw.c void ShowChar (int , int , chtype); //Local Module void ScrollUp_V (int , int); //Local Module .5结构体命名 结构体类型命名必须全部用大写字母,原则上前面以下划线开始;结构体变量命名必须用大小写字母组合,第一个字母必须使用大写字母,必要时可用下划线间隔。对于私有数据区,必须注明其所属的进程。全局数据定义只需注意其用途。 示例如下: typedef struct { char szProductName[20]; char szAuthor[20]; char szReleaseDate[16]; char szVersion[10]; unsigned long MaxTables; unsigned long UsedTables; }DBS_DATABASE; DBS_DATABASE GdataBase; 6 控件的命名: 用小写前缀表示类别: fm 窗口 cmd 按钮 cob combo,下拉式列表框 txt 文本输入框 lab labal,标签 img image,图象 pic picture grd Grid,网格 scr 滚动条 lst 列表框 frm fram 7注释 原则上注释要求使用中文; 文件开始注释内容包括:公司名称、版权、作者名称、时间、模块用途、背景介绍等,复杂的算法需要加上流程说明; 函数注释包括:输入、输出、函数描述、流程处理、全局变量、调用样例等,复杂的函数需要加上变量用途说明; 程序中注释包括:修改时间和作者、方便理解的注释等; 引用一: 文件开头的注释模板 /****************************************************************** ** 文件名: ** Copyright (c) 1998-1999 *********公司技术开发部 ** 创建人: ** 日 期: ** 修改人: ** 日 期: ** 描 述: ** ** 版 本: **----------------------------------------------------------------------------- ******************************************************************/ 引用二: 函数开头的注释模板 /***************************************************************** ** 函数名: ** 输 入: a,b,c ** a--- ** b--- ** c--- ** 输 出: x--- ** x 为 1, 表示... ** x 为 0, 表示... ** 功能描述: ** 全局变量: ** 调用模块: ** 作 者: ** 日 期: ** 修 改: ** 日 期: ** 版本 ****************************************************************/ 引用三: 程序中的注释模板 /*----------------------------------------------------------*/ /* 注释内容 */ /*----------------------------------------------------------*/ 8 程序 a. 程序编码力求简洁,结构清晰,避免太多的分支结构及太过于技巧性的程序,尽量不采用递归模式。 b. 编写程序时,亦必须想好测试的方法,换句话说,”单元测试” 的测试方案应在程序编写时一并拟好。 c. 注释一定要与程序一致。 d. 版本封存以后的修改一定要将老语句用/* */ 封闭,不能自行删除或修改,并要在文件及函数的修改记录中加以记录。 e. 程序中每个block 的开头 ”{" 及 "}” 必须对齐,嵌套的block 每进一套,缩进一个tab,TAB 为4个空格,block类型包括if、for、while、do等关键字引出的。 f. 对于比较大的函数,每个block 和特殊的函数调用,都必须注明其功能,举例如下: count.divisor = 1193280 / freq; // compute the proper count OutByte((unsigned short)67, (unsigned char)182); // tell 8253 that acount is coming OutByte((unsigned short)66, count. c[0]); // send low-order byte OutByte((unsigned short)66, count. c[1]); // send high-order byte ××××××××××××××××××××××××××××××××××××××× bcb,delphi中的变量命名: 遵循匈牙利命名法,命名必须有意义,制定如下规定 窗体: 以大写的W开始,如About版权窗体, 命名为WAbout 文件:以大写的F开始,如About版权窗体,文件命名为FAbout.cpp 按钮(Button):如退出按钮,命名为btnExit …… 基类: 加base标记,如报表基类,窗体命名为:WBaseRep, 文件命名为FBaseRep.cpp |
|
|