2009年8月22日
2009年6月2日
1、编辑MySQL(和PHP搭配之最佳组合)配置文件:
windows环境中:%MySQL(和PHP搭配之最佳组合)_installdir%\my.ini //一般在MySQL(和PHP搭配之最佳组合)安装目录下有my.ini即MySQL(和PHP搭配之最佳组合)的配置文件。 linux环境中:/etc/my.cnf
在[MySQL(和PHP搭配之最佳组合)d]配置段添加如下一行: skip-grant-tables
保存退出编辑。
2、然后重启MySQL(和PHP搭配之最佳组合)服务
windows环境中: net stop MySQL(和PHP搭配之最佳组合) net start MySQL(和PHP搭配之最佳组合)
linux环境中: /etc/init.d/MySQL(和PHP搭配之最佳组合)d restart
3、设置新的ROOT密码
然后再在命令行下执行: MySQL(和PHP搭配之最佳组合) -uroot -p MySQL(和PHP搭配之最佳组合) 直接回车无需密码即可进入数据库了。
现在我们执行如下语句把root密码更新为 7758521: update user set password=PASSWORD("7758521") where user='root';
quit 退出MySQL(和PHP搭配之最佳组合)。
4、还原配置文件并重启服务
然后修改MySQL(和PHP搭配之最佳组合)配置文件把刚才添加的那一行删除。
再次重起MySQL(和PHP搭配之最佳组合)服务,密码修改完毕。
修改完毕。
用新密码7758521试一下吧,又能登入MySQL(和PHP搭配之最佳组合)的感觉就是不一样吧?
2009年4月15日
一.什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。
二.字节对齐对程序的影响:
先让我们看几个例子吧(32bit,x86环境,gcc编译器): 设结构体如下定义: struct A { int a; char b; short c; }; struct B { char b; int a; short c; }; 现在已知32位机器上各种数据类型的长度如下: char:1(有符号无符号同) short:2(有符号无符号同) int:4(有符号无符号同) long:4(有符号无符号同) float:4 double:8 那么上面两个结构大小如何呢? 结果是: sizeof(strcut A)值为8 sizeof(struct B)的值却是12
结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。 之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如: #pragma pack (2) /*指定按2字节对齐*/ struct C { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ sizeof(struct C)值是8。 修改对齐值为1: #pragma pack (1) /*指定按1字节对齐*/ struct D { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ sizeof(struct D)值为7。 后面我们再讲解#pragma pack()的作用.
三.编译器是按照什么样的原则进行对齐的?
先让我们看四个重要的基本概念: 1.数据类型自身的对齐值: 对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。 2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。 3.指定对齐值:#pragma pack (value)时的指定对齐值value。 4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。 有 了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是 表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数 据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数 倍,结合下面例子理解)。这样就不能理解上面的几个例子的值了。 例子分析: 分析例子B; struct B { char b; int a; short c; }; 假 设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定 对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4, 所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为 2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的 都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;其实如果就这一个就来说它已将满足字节对齐了, 因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那 么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一 个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据,其 自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,这些已有类型的自身对齐值也是基于数组考虑的,只 是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了. 同理,分析上面例子C: #pragma pack (2) /*指定按2字节对齐*/ struct C { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ 第 一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放 在0x0006、0x0007中,符合 0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C 只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.
四.如何修改编译器的默认对齐值?
1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认是8字节。 2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.
五.针对字节对齐,我们在编程中如何考虑?
如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照 类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做 法是显式的插入reserved成员: struct A{ char a; char reserved[3];//使用空间换时间 int b; }
reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.
六.字节对齐可能带来的隐患:
代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如: unsigned int i = 0x12345678; unsigned char *p=NULL; unsigned short *p1=NULL;
p=&i; *p=0x00; p1=(unsigned short *)(p+1); *p1=0x0000; 最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。 在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.
七.如何查找与字节对齐方面的问题:
如果出现对齐或者赋值问题首先查看 1. 编译器的big little端设置 2. 看这种体系本身是否支持非对齐访问 3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。
八.相关文章:转自http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx
ARM下的对齐处理 from DUI0067D_ADS1_2_CompLib
3.13 type qulifiers
有部分摘自ARM编译器文档对齐部分
对齐的使用: 1.__align(num) 这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时 就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。 这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节 对齐,但是不能让4字节的对象2字节对齐。 __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。 2.__packed __packed是进行一字节对齐 1.不能对packed的对象进行对齐 2.所有对象的读写访问都进行非对齐访问 3.float及包含float的结构联合及未用__packed的对象将不能字节对齐 4.__packed对局部整形变量无影响 5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定 义为packed。 __packed int* p; //__packed int 则没有意义 6.对齐或非对齐读写访问带来问题 __packed struct STRUCT_TEST { char a; int b; char c; } ; //定义如下结构此时b的起始地址一定是不对齐的 //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL] //将下面变量定义成全局静态不在栈上 static char* p; static struct STRUCT_TEST a; void Main() { __packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
p = (char*)&a; q = (int*)(p+1); *q = 0x87654321; /* 得到赋值的汇编指令很清楚 ldr r5,0x20001590 ; = #0x12345678 [0xe1a00005] mov r0,r5 [0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函数 [0xe5c10000] strb r0,[r1,#0] //函数进行4次strb操作然后返回保证了数据正确的访问 [0xe1a02420] mov r2,r0,lsr #8 [0xe5c12001] strb r2,[r1,#1] [0xe1a02820] mov r2,r0,lsr #16 [0xe5c12002] strb r2,[r1,#2] [0xe1a02c20] mov r2,r0,lsr #24 [0xe5c12003] strb r2,[r1,#3] [0xe1a0f00e] mov pc,r14 */
/* 如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败 [0xe59f2018] ldr r2,0x20001594 ; = #0x87654321 [0xe5812000] str r2,[r1,#0] */
//这样可以很清楚的看到非对齐访问是如何产生错误的 //以及如何消除非对齐访问带来问题 //也可以看到非对齐访问和对齐访问的指令差异导致效率问题 }
2009年1月20日
关于文本的输出
在如何自己编写文本控件时,有关于如何展开Tab的具体做法:
1 void TextView::PaintLine(HDC hdc,int line_no) 2 3  { 4 5 int length = document->GetLineLength(line_no + start_line_no); 6 7 char *buffer = new char[length]; 8 9 document->GetLineBuffer(buffer,line_no + start_line_no); 10 11 //容纳单行文本矩形区域 12 13 RECT line_rect; 14 15 GetClientRect(tv_hwnd,&line_rect); 16 17 line_rect.top = line_no*(font_height + font_extra); 18 19 line_rect.bottom = line_rect.top + font_height + font_extra; 20 21 line_rect.left -= start_column_no*5; 22 23 //展开Tab字符 24 25 int tab = 4*font_width; 26 27 int width = TabbedTextOut(hdc,line_rect.left,line_rect.top,buffer,length,1,&tab,line_rect.left);//输出文字 28 29 line_rect.left = LOWORD(width); 30 31 ExtTextOut(hdc,0,0,ETO_OPAQUE,&line_rect,0,0,0); 32 33 delete []buffer; 34 35 } 36
要明白他的意思才能在以后的编写扩展功能才能得心应手。首先要明白一个函数。
TabbedTextOut
功能:
1将一个字符串写到指定位置。
2并按制表位位置数组里的值展开制表符。
函数原型:
LONG TabbedTextOut(HDC hdc, int X, int Y, LPCTSTR lpString, int nCount, int nTabPositions, LPINT lpn TabStopPositions, int nTabOrigin)
参数意义:
Hdc :设备环境句柄。
X: 字符串开始点的x坐标(逻辑单位)。
Y: 字符串开始点的y坐标(逻辑单位)。
lpString:缓冲区指针。
nCount: 字符数。
nTabPositions:指定制表位位置数组的值的个数。
lpnTabStopPositions:数组,包含制表位位置(逻辑单位)。必须按照升序保存。
nTabOrigin:指定制表符展开的开始位置的x坐标(逻辑单位)。
返回值:字符串的尺寸,高位字表示高度,低位表示宽度。
注:
【1】 如果nTabPositions值为0,且lpnTabStopPositions值位NULL,那么制表符会按平均字符宽度的8位来扩展。
【2】 如果lpnTabStopPositions数组包含一个以上的话,则制表位被设为数组里的每一个值,共为lpnTabStopPositions个。
【3】 nTabOrigin参数允许一个应用程序为一行多次调用TabbedTextOut。如果应用程序多次调用TabbedTextOut,nTabOrigin每次都设置相同的值,则此函数在相对于nTabOrigin指定的位置处展开所有的制表符。
知识补充:
TabbedTextOut(hdc,line_rect.left,line_rect.top,buffer,length,1,&tab,line_rect.left);
第六个参数为nTabPosition = 1
第七个参数为lpnTabStopPositions = tab = 4*font_width
第八个参数位nTabOrigin = line_rect.left
因为编辑器以行位模型,当然是从一行的最左端开始。制表位数组值一个等于字体宽度的4倍。
摘自《windows编程》的解释:
TabbedTextOut的前五个参数与TextOut相同,第六个参数是跳位间隔数,第七个是以图素为单位的跳位间隔数组。
【1】 如果平均字符宽度是8个图素,而您希望每5个字符加一个跳位间隔,则这个数组将包含40、80、120,按递增顺序依此类推。
【2】 如果第六个和第七个参数是0或NULL,则跳位间隔按每八个平均字符宽度设定。
【3】 如果第六个参数是1,则第七个参数指向一个整数,表示跳位间隔重复增大的倍数(例如,如果第六个参数是1,并且第七个参数指向值为30的变量,则跳位间隔设定在30、60、90…图素处)。最后一个参数给出了从跳位间隔开始测量的逻辑x坐标,它与字符串的起始位置可能相同也可能不同。
编辑器的展开tab属于【3】,增大倍数是4个字符宽度。4倍与8倍的区别如图:
2008年11月16日
继续上个版本修改了一些bug,然后美化了一下。
发现自己的审美观不咋的,俺觉得漂亮的人家觉得不漂亮。还是照着大家要求的画一个。
主要解决的问题是,这次行列都用宏表示,这样可以修改行列,窗口大小也动态改变。
另外长条旋转变成Z型问题也解决,主要是取模的时候绕回去了。
其中最重要的要算是解决了刷新闪烁问题,尽管HAM2008指点过,始终没做成,这次vczh说了一句话就点醒了我。根本不应该使用InvalidateRect函数,直接画,然后用缓冲DC就可以了。
VOID OnPaint()
  {
HDC hdc = GetDC(hWnd);
HDC bitmap_dc = CreateCompatibleDC(hdc);
HBITMAP bitmap = CreateCompatibleBitmap(hdc,1024,768);
SelectObject(bitmap_dc,bitmap);
 /**//********************************************
DrawBlock
*********************************************/
int x = tetris.GetX();
int y = tetris.GetY();

for(int i=0; i<4; ++i)
 {
for(int j=0; j<4; ++j)
 {
if(current_block[i][j] == 1)
 {
DrawBlock(bitmap_dc,y+i+1,x+j+1,3,3,tetris.GetColor(),RGB(0,0,0));
}
}
}

 /**//*****************************************
*DrawContainer
******************************************/
for(int i=0; i<ROWS; ++i)
 {
for(int j=0; j<COLS; ++j)
 {
if(Container[i][j] == 1)
 {
DrawBlock(bitmap_dc,i+1,j+1,3,3,ColorTable[i][j],RGB(0,0,0));
}
}
}

BitBlt(hdc,0,0,1024,768,bitmap_dc,0,0,SRCCOPY);
DeleteDC(bitmap_dc);
DeleteObject(bitmap);
ReleaseDC(hWnd,hdc);
}
以上就是GDI缓冲的主要实现代码。
可执行文件下载代码还是等全部完善后上传吧。Redist请自行下载。 代码估计要有大的改动,感觉现在的代码没一点C++的味道。 有点简单,用陈坤的话说就是扩展性不好。
2008年11月9日
2008年10月26日
C++博客上的蚂蚁终结者的文章都写的不错,它写了个MD5的算法,我就用他的算法写了个小程序。 简单的计算文件的MD5,支持拖拽文件。现在还只是简单的计算。 目前打算改进的是 1添加进度条。 2美化界面。 3解决大文件计算时界面僵死问题。
 个人写程序喜欢不断改进,也就是偶然突发奇想写个程序。以后想起来就改进一下。嘎嘎。 要是忘记了,就作罢。代码可点击这里下载
2008年10月3日
脑袋里没有点API的储量,想写什么东西还是很困难的。厚积薄发才是硬道理。所以先看看别人的代码,偷学几个API的用法。
SetClipData proc lpData:LPSTR,dwSize:dword
LOCAL hMem:HANDLE ;==>内存块句柄
LOCAL pMem:dword ;==>内存块指针

mov eax,dwSize
shr eax,3
inc eax
shl eax,3 ;==>(dwSize/8 + 1)*8 不满8的倍数则补全
invoke xGlobalAlloc, GHND or GMEM_DDESHARE, eax
test eax,eax ;==>申请成功则继续否则跳转到@exit2
je @exit2
mov hMem,eax
invoke GlobalLock,eax ;hGlob ;==>锁定申请的内存块返回内存块指针
test eax,eax
je @exit1
mov pMem,eax
invoke RtlMoveMemory,eax,lpData,dwSize ;==>复制lpData的内容到申请的内存块中
mov eax,pMem
add eax,dwSize
mov byte ptr [eax],0 ;==>在内存块最后添0结束
invoke GlobalUnlock,hMem ;==>解锁,使内存块指针无效
invoke OpenClipboard,NULL
.if eax
invoke EmptyClipboard
invoke SetClipboardData,CF_TEXT,hMem ;==>将数据关联到剪贴板
invoke CloseClipboard
xor eax,eax ;0 - Ok
jmp @exit3
.endif
@exit1:
invoke GlobalFree, hMem ;==>未锁定成功则释放该内存块
xor eax, eax
@exit2:
dec eax ; -1 - error
@exit3:
ret

SetClipData endp
看完这段代码我有一个疑问,这里是将lpData的数据拷贝到一个内存块,然后与剪贴板关联,我这里解释为关联,我就认为剪贴板不应该是一个内存区域,我猜测是一个链表之类的结构,然后链表每一个节点存储一个内存区域的指针还有其他的信息,然后根据信息来管理。
否则应该可以直接将lpData来跟剪贴板关联。另一个原因是lpData是局部的随时会被释放的。如有不正确还望指正。
2008年10月2日
通常我们在调用DLL时所需的DLL文件必须位于以下三个目录之一: (1)Windows的系统目录:\windows\system; (2)DOS中path所指出的任何目录; (3)程序所在的目录。 一、动态链接库的结构
动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function), 导出函数可以被其它模块调用,内部函数只能在库内部使用。我们在用C++定制动态库文件时, 需要编写的就是包含导出函数表的模块定义文件(.DEF)和实现导出函数功能的C++文件。下面以 Sample.dll为例介绍DEF文件和实现文件的结构。 1.模块定义文件(.DEF)是一个或多个用于描述DLL属性的模块语句组成的文本文件,每个DEF文 件至少必须包含以下模块定义语句: ·第一个语句必须是LIBRARY语句,指出DLL的名字; ·EXPORTS语句列出被导出函数的名字; ·可以使用DESCRIPTION语句描述DLL的用途(此句可选); ·";"对一行进行注释(可选)。 2.实现文件 实现入口表函数的cpp文件中,包含DLL入口点处理的API函数和导出函数的代码。 二、创建Sample.dll 1.首先创建Sample.dll的工程,启动VC++5.0按以下步骤生成DLL工程: ·在选单中选择File\New\Project; ·在工程列表中选择Win32 Dynamic-Link Library; ·在Project Name中输入工程名:Sample; ·单击Location右边按钮,选择c:\sample目录; ·单击OK完成,至此已创建了Sample.dll的工程文件。 2.创建Sample.def文件: ·在选单中选择File\New\Text File; ·输入以下代码后保存文件名"Sample.def": ;Sample.def ;指出DLL的名字Sample,链接器将这个名字放到DLL导入库中 LIBRARY Sample ;定义导出函数ShowMe()为例
EXPORTS ShowMe ;def文件结束 3.创建Sample.cpp .在选单中选择File\New\C++ Source File项 .输入以下代码后保存文件名"Sample.cpp" //Sample.cpp #include 〈windows.h〉 int ShowMe(void); //DllEntryPoint为DLL入口点函数,负责初试化并终止DLL BOOL WINAPI DllEntryPoint(HINSTANCE hDLL,DWORD dwReason,LPVOID Reserved) { switch(dwReason) { case DLL-PROCESS-ATTACH: { break; } case DLL-PROCESS-DETACH: { break; } } return TRUE; } int ShowMe(void) { //蜂鸣器响一下 MessageBeep((WORD)-1); MessageBox("你好!"); return 1; } 4.编译DLL文件 从Build选单中选择Build Sample.DLL,产生Sample.DLL文件,以后就可以随时调用了。 三、在应用程序中调用DLL文件 在应用程序中要首先装入DLL后才能调用导出表中的函数,例如用MFC创建基于对话框的工 程Test,并在对话框上放置"Load"按钮,你就必须添加装载代码。 1.首先在TestDlg.cpp的首部添加变量设置代码: //设置全局变量gLibSample用于存储DLL句柄 HINSTANCE gLibSample=NULL; //第二个变量ShowMe是指向DLL库中ShowMe()函数的指针 typedef int( SHOWME)(void);
SHOWME ShowMe; 2.利用ClassWizard为"Load"按钮添加装载DLL的代码: Void CTestDlg::OnLoadButton() { //要添加的代码如下 if(gLibMyDLL!=NULL) { MessageBox("The Sample.DLL has already been load."); return; } //装载Sample.dll,未加路径,将在三个默认路径中寻找 gLibSample=LoadLibrary("SAMPLE.DLL"); //返回DLL中ShowMe()函数的地址 ShowMe=(SHOWME)GetProcAddress(gLibSample,"ShowMe"); } 3.只要DLL装载成功,在应用程序中就可以直接调用ShowMe()函数(本程序在Windows 95,VC++5.0中运行通过)。
2008年9月29日
“又是一年毕业时”,看到一批批学子离开人生的象牙塔,走上各自的工作岗位;想想自己也曾经意气风发、踌躇满志,不觉感叹万千……本文是自己工作6年的经历沉淀或者经验提炼,希望对所有的软件工程师们有所帮助,早日实现自己的人生目标。本文主要是关于软件开发人员如何提高自己的软件专业技术方面的具体建议。
1、 分享第一条经验:“学历代表过去、能力代表现在、学习力代表未来。”其实这是一个来自国外教育领域的一个研究结果。相信工作过几年、十几年的朋友对这个道理有些体会吧。但我相信这一点也很重要:“重要的道理明白太晚将抱憾终生!”所以放在每一条,让刚刚毕业的朋友们早点看到哈!
2、 一定要确定自己的发展方向,并为此目的制定可行的计划。不要说什么,“我刚毕业,还不知道将来可能做什么?”,“跟着感觉走,先做做看”。因为,这样的观点会通过你的潜意识去暗示你的行为无所事事、碌碌无为。一直做技术,将来成为专家级人物?向管理方向走,成为职业经理人?先熟悉行业和领域,将来自立门户?还是先在行业里面混混,过几年转行做点别的?这很重要,它将决定你近几年、十年内“做什么事情才是在做正确的事情!”。
3、 软件开发团队中,技术不是万能的,但没有技术是万万不能的!在技术型团队中,技术与人品同等重要,当然长相也比较重要哈,尤其在MM比较多的团队中。在软件项目团队中,技术水平是受人重视和尊重的重要砝码。无论你是做管理、系统分析、设计、编码,还是产品管理、测试、文档、实施、维护,多少你都要有技术基础。算我孤陋寡闻,我还真没有亲眼看到过一个外行带领一个软件开发团队成功地完成过软件开发项目,哪怕就一个,也没有看到。倒是曾经看到过一个“高学历的牛人”(非技术型)带一堆人做完过一个项目,项目交付的第二天,项目组成员扔下一句“再也受不了啦!”四分五裂、各奔东西。那个项目的“成功度”大家可想而知了。
4、 详细制定自己软件开发专业知识学习计划,并注意及时修正和调整(软件开发技术变化实在太快)。请牢记:“如果一个软件开发人员在1、2年内都没有更新过自己的知识,那么,其实他已经不再属于这个行业了。”不要告诉自己没有时间。来自时间管理领域的著名的“三八原则”告诫我们:另外的那8小时如何使用将决定你的人生成败!本人自毕业以来,平均每天实际学习时间超过2小时。
5、 书籍是人类进步的阶梯,对软件开发人员尤其如此。书籍是学习知识的最有效途径,不要过多地指望在工作中能遇到“世外高人”,并不厌其烦地教你。对于花钱买书,我个人经验是:千万别买国内那帮人出的书!我买的那些家伙出的书,!00%全部后悔了,无一本例外。更气愤的是,这些书在二手市场的地摊上都很难卖掉。“拥有书籍并不表示拥有知识;拥有知识并不表示拥有技能;拥有技能并不表示拥有文化;拥有文化并不表示拥有智慧。”只有将书本变成的自己智慧,才算是真正拥有了它。
6、 不要仅局限于对某项技术的表面使用上,哪怕你只是偶尔用一、二次。“对任何事物不究就里”是任何行业的工程师所不应该具备的素质。开发Windows应用程序,看看Windows程序的设计、加载、执行原理,分析一下PE文件格式,试试用SDK开发从头开发一个Windows应用程序;用VC++、Delphi、Java、.Net开发应用程序,花时间去研究一下MFC、VCL、J2EE、.Net它们框架设计或者源码;除了会用J2EE、JBoss、Spring、Hibernate等等优秀的开源产品或者框架,抽空看看大师们是如何抽象、分析、设计和实现那些类似问题的通用解决方案的。试着这样做做,你以后的工作将会少遇到一些让你不明就里、一头雾水的问题,因为,很多东西你“知其然且知其所以然”!
7、 在一种语言上编程,但别为其束缚了思想。“代码大全”中说:“深入一门语言编程,不要浮于表面”。深入一门语言开发还远远不足,任何编程语言的存在都有其自身的理由,所以也没有哪门语言是“包治百病”的“灵丹妙药”。编程语言对开发人员解决具体问题的思路和方式的影响与束缚的例子俯拾皆是。我的经验是:用面对对象工具开发某些关键模块时,为什么不可以借鉴C、C51、汇编的模块化封装方式?用传统的桌面开发工具(目前主要有VC++、Delphi)进行系统体统结构设计时,为什么不可以参考来自Java社区的IoC、AOP设计思想,甚至借鉴像Spring、Hibernate、JBoss等等优秀的开源框架?在进行类似于实时通信、数据采集等功能的设计、实现时,为什么不可以引用来自实时系统、嵌入式系统的优秀的体系框架与模式?为什么一切都必须以个人、团队在当然开发语言上的传统或者经验来解决问题???“他山之石、可以攻玉”。
8、 养成总结与反思的习惯,并有意识地提炼日常工作成果,形成自己的个人源码库、解决某类问题的通用系统体系结构、甚至进化为框架。众所周知,对软件开发人员而言,有、无经验的一个显著区别是:无经验者完成任何任务时都从头开始,而有经验者往往通过重组自己的可复用模块、类库来解决问题(其实这个结论不应该被局限在软件开发领域、可以延伸到很多方面)。这并不是说,所有可复用的东西都必须自己实现,别人成熟的通过测试的成果也可以收集、整理、集成到自己的知识库中。但是,最好还是自己实现,这样没有知识产权、版权等问题,关键是自己实现后能真正掌握这个知识点,拥有这个技能。
9、 理论与实践并重,内外双修。工程师的内涵是:以工程师的眼光观察、分析事物和世界。一个合格的软件工程师,是真正理解了软件产品的本质及软件产品研发的思想精髓的人(个人观点、欢迎探讨)。掌握软件开发语言、应用语言工具解决工作中的具体问题、完成目标任务是软件工程师的主要工作,但从软件工程师这个角度来看,这只是外在的东西,并非重要的、本质的工作。学习、掌握软件产品开发理论知识、软件开发方法论,并在实践中理解、应用软件产品的分析、设计、实现思想来解决具体的软件产品研发问题,才是真正的软件工程师的工作。站在成熟理论与可靠方法论的高度思考、分析、解决问题,并在具体实践中验证和修正这些思想与方式,最终形成自己的理论体系和实用方法论。
10、心态有多开放,视野就有多开阔。不要抱着自己的技术和成果,等到它们都已经过时变成垃圾了,才拿出来丢人现眼。请及时发布自己的研究成果:开发的产品、有创意的设计或代码,公布出来让大家交流或者使用,你的成果才有进化和升华的机会。想想自己2000年间开发的那些Windows系统工具,5、6年之后的今天,还是那个样子,今天流行的好多Windows系统工具都比自己的晚,但进化得很好,且有那么多用户在使用。并且,不要保守自己的技术和思想,尽可能地与人交流与分享,或者传授给开发团队的成员。“与人交换苹果之后,每个人还是只有一个苹果;但交换思想之后,每个人都拥有两种思想”,道理大家都懂,但有多少人真正能做到呢?
11、尽量参加开源项目的开发、或者与朋友共同研制一些自己的产品,千万不要因为没有钱赚而不做。网络早已不再只是“虚拟世界”,网上有很多的开源项目、合作开发项目、外包项目,这都是涉猎工作以外的知识的绝好机会,并且能够结识更广的人缘。不要因为工作是做ERP,就不去学习和了解嵌入式、实时、通信、网络等方面的技术,反过来也是一样。如果当他别人拿着合同找你合作,你却这也不会,那也不熟时,你将后悔莫及。
12、书到用时方恨少,不要将自己的知识面仅仅局限于技术方面。诺贝尔经济学奖得主西蒙教授的研究结果表明: “对于一个有一定基础的人来说,他只要真正肯下功夫,在6个月内就可以掌握任何一门学问。”教育心理学界为感谢西蒙教授的研究成果,故命名为西蒙学习法。可见,掌握一门陌生的学问远远没有想想的那么高难、深奥。多方吸取、广泛涉猎。极力夯实自己的影响圈、尽量扩大自己的关注圈。财务、经济、税务、管理等等知识,有空花时间看看,韬光养晦、未雨绸缪。
13、本文的总结与反思:
A:不要去做技术上的高手,除非你的目标如此。虽然本文是关于提高软件开发知识的建议,做技术的高手是我一向都不赞同的。你可以提高自己的专业知识,但能胜任工作即止。
B:提高软件知识和技术只是问题的表面,本质是要提高自己认识问题、分析问题、解决问题的思想高度。软件专业知识的很多方法和原理,可以很容易地延伸、应用到生活的其它方面。
C:在能胜任工作的基础上,立即去涉猎其它领域的专业知识,丰富自己的知识体系、提高自己的综合素质,尤其是那些目标不在技术方面的朋友。
2008年9月7日
vector优势:1.随机访问 2.在尾部插入删除元素
#include<iostream>
#include<vector>
using namespace std;

void Print(vector<int>& vec)
  {
for(int i = 0; i < vec.size(); ++i)cout<<' '<<vec[i];
cout<<endl;
}

int main()
  {
vector<int> first;
vector<int> second(4, 1000);
vector<int> third(second.begin() + 2, second.end());
vector<int> forth(third);

vector<int>::iterator it;

 int val[] = {1,2,3,4};
vector<int> fifth(val, val + sizeof(val) / sizeof(val[0]));

cout<<"First:";
Print(first);
cout<<"Second:";
Print(second);
cout<<"Third:";
Print(third);
cout<<"Forth:";
Print(forth);
cout<<"Fifth:";
Print(fifth);

first.swap(fifth);
cout<<"First:";
Print(first);

first.push_back(12);
cout<<"First:";
Print(first);

it = first.begin() + 2;
first.erase(it,first.end());
cout<<"First:";
Print(first);

it = first.begin();
first.insert(it,100);
cout<<"First:";
Print(first);

system("pause");
return 0;
}
结果:  Deque操作代码类似。 优势:比之Vector在头部插入删除元素也有很高效率。也支持迭代器随机访问。不过元素在内存中不连续。 List操作基本相同不过多了一些功能 优势:高效遍历元素,常量时间插入删除任意位置元素。
#include<iostream>
#include<list>
using namespace std;

void Print(list<int>& ls)
  {
list<int>::iterator it = ls.begin();
for(; it != ls.end(); ++it)cout<<' '<<*it;
cout<<endl;
}

void Print(list<double>& ls)
  {
list<double>::iterator it = ls.begin();
for(; it != ls.end(); ++it)cout<<' '<<*it;
cout<<endl;
}

int main()
  {
list<int> first;
list<int> second(4, 1000);

list<int>::iterator it;

 double first_val[] = {1.0,3.0,2.0,4.0};
list<double> third(first_val, first_val + sizeof(first_val) / sizeof(first_val[0]));

 double sencond_val[] = {1.1,4.3,1.4,2.9};
list<double> fourth(sencond_val, sencond_val + sizeof(sencond_val)/sizeof(sencond_val[0]));

it = first.begin();
first.insert(it,100);
cout<<"First:";
Print(first);

it = first.begin();
first.splice(it,second);//splice四个参数,第一个参数是插入的位置,第二个是插入源,第三四个参数指定范围
cout<<"First:";
Print(first);

first.remove(100);
cout<<"First:";
Print(first);

third.sort();
fourth.sort();
cout<<"Third:";
Print(third);
cout<<"Fourth:";
Print(fourth);

third.merge(fourth);
cout<<"Third:";
Print(third);

system("pause");
return 0;
}
结果:  splice在代码中已经说明,merge函数合并两个list而且是按照从小到大的顺序,merge有另一个版本包含两个参数,另一个是一个 返回bool类型的函数,说明了比较规则。用法相同。另外一些函数使用比较简单。 该类笔记均参考: www.cplusplus.com
2008年8月31日
1)运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数. 2)C 语言是所谓的“小内核”语言,就其语言本身来说很小(不多的关键字,程序流程控制,数据类型等);所以,C 语言内核开发出来之后,Dennis Ritchie 和 Brian Kernighan 就用 C 本身重写了 90% 以上的 UNIX 系统函数,并且把其中最常用的部分独立出来,形成头文件和对应的 LIBRARY,C run-time library 就是这样形成的。 3)随后,随着 C 语言的流行,各个 C 编译器的生产商/个体/团体都遵循老的传统,在不同平台上都有相对应的 Standard Library,但大部分实现都是与各个平台有关的。由于各个 C 编译器对 C 的支持和理解有很多分歧和微妙的差别,所以就有了 ANSI C;ANSI C (主观意图上)详细的规定了 C 语言各个要素的具体含义和编译器实现要求,引进了新的函数声明方式,同时订立了 Standard Library 的标准形式。所以C运行时库由编译器生产商提供。至于由其他厂商/个人/团体提供的头文件和库函数,应当称为第三方 C 运行库(Third party C run-time libraries)。 4)C run-time library里面含有初始化代码,还有错误处理代码(例如divide by zero处理)。你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。因为C run-time library包含了C程序运行的最基本和最常用的函数。 5)到了 C++ 世界里,有另外一个概念:Standard C++ Library,它包括了上面所说的 C run-time library 和 STL。包含 C run-time library 的原因很明显,C++ 是 C 的超集,没有理由再重新来一个 C++ run-time library. VC针对C++ 加入的Standard C++ Library主要包括:LIBCP.LIB, LIBCPMT.LIB和 MSVCPRT.LIB 6)Windows环境下,VC提供的 C run-time library又分为动态运行时库和静态运行时库。 动态运行时库主要是DLL库文件msvcrt.dll(or MSVCRTD.DLL for debug build),对应的Import library文件是MSVCRT.LIB(MSVCRTD.LIB for debug build) 静态运行时库(release版)对应的主要文件是: LIBC.LIB (Single thread static library, retail version) LIBCMT.LIB (Multithread static library, retail version) msvcrt.dll提供几千个C函数,即使是像printf这么低级的函数都在msvcrt.dll里。其实你的程序运行时,很大一部分时间时在这些运行库里运行。在你的程序(release版)被编译时,VC会根据你的编译选项(单线程、多线程或DLL)自动将相应的运行时库文件(libc.lib,libcmt.lib或Import library msvcrt.lib)链接进来。 编译时到底哪个C run-time library联入你的程序取决于编译选项: /MD, /ML, /MT, /LD (Use Run-Time Library) 你可以VC中通过以下方法设置选择哪个C run-time library联入你的程序: To find these options in the development environment, click Settings on the Project menu. Then click the C/C++ tab, and click Code Generation in the Category box. See the Use Run-Time Library drop-down box. 从程序可移植性考虑,如果两函数都可完成一种功能,选运行时库函数好,因为各个 C 编译器的生产商对标准C Run-time library提供了统一的支持.
2008年8月20日
最早时候就曾经哪里看到过说所有控件都是窗口(window),更有甚者说都是对象,这个就不扯了。自己做好的控件是做成Lib还是Dll那是后话,MFC我是不熟悉了,Win32还是看了几天的。大致把制作的整个流程简要的记录一下。
自己做的控件最主要的功能就是接受你发给他的命令,也就是要给外部调用的接口。控件有自己的消息处理函数比如
LRESULT CALLBACK PETextViewWndProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
  {
PETextView *View = (PETextView*)GetWindowLong(hWnd,0);

switch(Message)
 {
case WM_NCCREATE:
if((View = new PETextView(hWnd)) == 0)
return false;

SetWindowLong(hWnd,0,(LONG)View);
return true;

case WM_NCDESTROY:
if(View)delete View;
return 0;

case WM_PAINT:
return View->OnPaint();

case WM_SIZE:
return View->OnSize(wParam, LOWORD(lParam), HIWORD(lParam));

case PEM_OPENFILE:
return View->OpenFile((TCHAR*)lParam);

case PEM_CLEAR:
return View->ClearFile();
default:
break;
}

return DefWindowProc(hWnd,Message,wParam,lParam);
}

这里有两类消息,一类是系统定义的以WM开头,一类是自己定义的,当然随便你自己定义啦。对应的消息看到是调用相应的函数完成的,这也就是说控件的行为就可以另外编写逻辑部分,然后提供接口给这里调用即可。
之所以说控件就是窗口是因为他有自己的窗口类,以及初始化函数,同时也有创建的函数。窗口类的定义和注册也做成提供给外部的接口,在外部必要的时候调用。而控制控件是通过发送消息来实现的,为了更加好看,可以定义一个宏,比如
#define PE_OpenFile(hWnd, Path) SendMessage((hWnd), PEM_OPENFILE, 0, (LPARAM)(Path))
那么创建窗口和控件唯一不同的地方就是,内部的创建和销毁消息是WM_NCCREATE和WM_NCDESTROY,先不管这两个消息。我们看到WM_NCCREATE之前有个GetWindowLong,其内有个SetWindowLong。这两个是关键的,这样就设置了这个控件的属性,使其关联起来,第二个参数msdn上是没有说明设置为0是什么意思的,其实这两个函数的第二个参数设置0表示读取的意思,第一个是读取该控件的属性,然后第二个函数在增加第三个参数的属性的同时读取赋予给这个控件。
一切都OK了!那么控件创建可在外部的WM_CREATE之时调用,当然也可以在使用其功能前调用即可。
一切提供给外部的调用都Port在一个头文件中,这样使用的时候包含这个头文件就好了。
接下来解释那两个消息,这两个消息是因为我们创建了子窗口,也就是我们自己的控件。
这两个消息与WM_CREATE,WM_DESTROY之间的顺序关系是这样的,只看销毁吧。
hwnd = parent, uMsg = WM_DESTROY hwnd = child, uMsg = WM_DESTROY hwnd = child, uMsg = WM_NCDESTROY hwnd = parent, uMsg = WM_NCDESTROY
WM_DESTROY是通知子窗口销毁,然后子窗口通过接受WM_NCDESTROY进行销毁,并发送给父窗口,进行销毁。
细节部分介绍的差不多,总体思路就是和创建窗口差不多,但是要搞个头文件,把一些个常量和功能的函数另外一个窗口类的初始化和创建的接口搞进去。
2008年8月6日
摘要: 第一次写俄罗斯方块,完全是按照自己的想法做的。做完了很奇怪。估计是按照相对坐标来算,好多的分支语句把自己都搞晕了。所以决定放弃了,贴出来以祭奠。设计的草稿是这样的
棋子记录状态
棋盘根据棋子状态进行判断和绘制
主要检测:
越界检测:每次左移或者右移时检测(在边界内则移动否则不动)OK
触底检测:每次下降时检测OK,也就是在时钟记录一次时探测。
消行检测:触底触发时检测
旋转检测:... 阅读全文
2008年8月4日
以前曾经推荐过大家一个MasmPlus是用于编写汇编程序的IDE,简洁轻巧。 可以到 这里下载 然而文本编辑器始终没有遇到一个简洁的,偶然机会看到这个还算符合要求,叫E-texteditor  同样小巧精悍。可惜不是免费的,不过crack倒是一搜一大堆。 VistualStudio一装我的硬盘就几乎没空间了。这回可以用他来编辑了. 编译器使用mingw,以前都是整个都下载下来,这回就把必要的部分下载下来,顺带make和gdb打了个包 这里可以下载。make和gdb手生的很,记得一些如codeblocks他们会自动生成makefile文件,不知道这个什么原理。
2008年7月29日
本文转自 Azure博客自己总是用VC平台来开发东西,但是有时候总是出这样那样的问题,呵呵,总是需要上网查资料来解决,在这里把自己用到上网查的一些技巧摘录如下,希望对大家有用,省去大家再去搜索的烦恼。 1.如何在Release状态下进行调试Project->Setting=>ProjectSetting对话框,选择Release状态。C/C++标签中的Category选General,Optimizations选Disable(Debug),Debut info选Program Database。在Link标签中选中Generate debug info复选框。 注:只是一个介乎Debug和Release的中间状态,所有的ASSERT、VERIFY都不起作用,函数调用方式已经是真正的调用,而不查表,但是这种状态下QuickWatch、调用队列跟踪功能仍然有效,和Debug版一样。 2. Release和Debug有什么不同Release版称为发行版,Debug版称为调试版。 Debug中可以单步执行、跟踪等功能,但生成的可执行文件比较大,代码运行速度较慢。Release版运行速度较快,可执行文件较小,但在其编译条件下无法执行调试功能。 Release的exe文件链接的是标准的MFC DLL(Use MFC in a shared or static dll)。这些DLL在安装Windows的时候,已经配置,所以这些程序能够在没有安装Visual C++ 6.0的机器上运行。而Debug版本的exe链接了调试版本的MFC DLL文件,在没有安装Visual C++6.0的机器上不能运行,因为缺相应的DLL,除非选择use static dll when link。 3. ASSERT和VERIFY有什么区别ASSERT里面的内容在Release版本中不编译,VERIFY里面的内容仍然翻译,但不再判断真假。所以后者更安全一点。 例如ASSERT(file.Open(strFileName))。 一旦到了Release版本中,这一行就忽略了,file根本就不Open()了,而且没有任何出错的信息。如果用VERIFY()就不会有这个问题。 4.Workspace和Project之间是什么样的关系每个Workspace可以包括几个project,但只有一个处于Active状态,各个project之间可以有依赖关系,在project的Setting..中可以设定,比如那个Active状态的project可以依赖于其他的提供其函数调用的静态库。 5. 如何在非MFC程序中使用ClassWizard在工程目录下新建一个空的.RC文件,然后加入到工程中就可以了。 6.如何设置断点按F9在当前光标处增加一个断点和取消一个断点。 另外,在编辑状态下,按Ctrl+B组合键,弹出断点设置对话框。然后单击【Condition…】按钮弹出设置断点条件的对话框进行设置。 7.在编辑状态下发现成员变量或函数不能显示提示是如何打开显示功能这似乎是目前这个Visual C++ 6.0版本的一个bug,可按如下步骤使其正常,如再出现,可如法炮制: (1)关闭Project (2)删除“工程名.ncb”文件 (3)重新打开工程 8.如何将一个通过ClassWizard生成的类彻底删除首先在工作区的FileView中选中该类的.h和.cpp文件,按delete删除,然后在文件管理器中将这两个文件删除,再运行ClassWizard,这时出现是否移走该类的提示,选择remove就可以了。 9. 如何将在workspace中消失的类找出来打开该类对应的头文件,然后将其类名随便改一下,这个时候工作区就会出现新的类,再将这个类改回原来的名字就可以了。 10. 如何清除所有的断点菜单【Edit】->【Breakpoints…】,打开“Breakpoints”对话框,单击【Remove All】按钮即可。快捷键是“Ctrl + Shift + F8”。 11. 如何再ClassWizard中选择未列出的信息打开“ClassWizard”对话框,然后切换到“Class Info”页面。改变“Message filter”,如选择“Window”,“Message”页面就会出现Window的信息。 12. 如何检测程序中的括号是否匹配把光标移动到需要检测的括号前面,按快捷键“Ctrl + ]”。如果括号匹配正确,光标就跳到匹配的括号处,否则光标不移动,并且机箱喇叭还会发出一声警告。 13. 如何查看一个宏(或变量、函数)的定义把光标移动到要查看的一个宏上,就比如说最常见的DECLARE_MAP_MESSAGE上按一下F12(或右键菜单中的相关菜单),如果没有建立浏览文件,就会出现提示对话框,按【确定】按钮,然后就会跳到该宏(或变量、函数)定义的地方。 14. 如何添加Lib文件到当前工程单击菜单【Project】->【Settings…】弹出“Project Setting”对话框,切换到“Link”标签页,在“Object/library modules”处输入Lib文件名称,不同的Lib之间用空格格开。 15. 如何快速删除项目下的Debug文件夹中临时文件在工作区的FileView视图中选中对应的项目,单击右键弹出菜单,选择【Clean(selection only)】菜单即可。 16. 如何快速生成一个现有工程除了工程名外完全相同的新工程在新建工程的“New”对话框中选择“Custom Appwizard”项,输入新工程的名字,单击【OK】按钮。出现“Custom AppWizard”项,输入新工程的名字,单击【OK】按钮。出现“Custom AppWizard-Step 1 of 2”对话框,选择“An existing Project”项,单击【Next】按钮。出现“Custom AppWizard-Step 2 of 2”对话框,选择现有工程的工程文件名,最后单击【Finish】按钮。编译后就生成一个与现有工程相同但可以重新取名的工程AppWizard。 现在就可以项用MFC AppWizard一样用这个定制的向导。如果不想用了,可以在Visual C++ 6.0安装目录下Common\MSDev98\Template目录中删除该Wizard对应的.awx和.pdb文件。 17. 如何解决Visual C++ 6.0不正确连接的问题情景:明明改动了一个文件,却要把整个项目全部重新编译链接一次。刚刚链接好,一运行,又提示重新编译链接一次。 这是因为出现了未来文件(修改时间和创建时间比系统时间晚)的缘故。可以这样处理:找到工程文件夹下的debug目录,将创建和修改时间都比系统时间的文件全部删除,然后再从新“Rebuild All”一次。 18. 引起LNK2001的常见错误都有哪些遇到的LNK2001错误主要为:unresolved external symbol “symbol” 如果链接程序不能在所有的库和目标文件内找到所引用的函数、变量或标签,将产生此错误信息。 一般来说,发生错误的原因有两个:一是所引用的函数、变量不存在,拼写不正确或者使用错误;其次可能使用了不同版本的链接库。以下是可能产生LNK2001错误的原因: <1>由于编码错误导致的LNK2001错误 (1)不相匹配的程序代码或模块定义(.DEF)文件导致LNK2001。例如,如果在C++源文件了内声明了一变量“var1”,却试图在另一个文件内以变量“var1”访问改变量。 (2)如果使用的内联函数是在.cpp文件内定义的,而不是在头文件内定义将导致LNK2001错误。 (3)调用函数时如果所用的参数类型和头函数声明时的类型不符将会产生LNK2001错误。 (4)试图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001错误。 (5)要注意函数和变量的可公用性,只有全局变量、函数是可公用的。静态函数和静态变量具有相同的使用范围限制。当试图从文件外部方位任何没有在该文件内声明的静态变量时将导致编译错误或LNK2001错误。 <2>由于编译和联机的设置而造成的LNK2001错误 (1)如果编译时使用的是/NOD(/NODERAULTLIB)选项,程序所需要的运行库和MFC时将得到又编译器写入目标文件模块,但除非在文件中明确包含这些库名,否则这些库不会被链接进工程文件。这种情况下使用/NOD将导致LNK2001错误 (2)如果没有为wWinMainCRTStartup设定程序入口,在使用Unicode和MFC时将出现“unresolved external on _WinMain@16”的LNK2001错误信息。 (3)使用/MD选项编译时,既然所有的运行库都被保留在动态链接库之内,源文件中对“func”的引用,在目标文件里即对“__imp__func”的引用。如果试图使用静态库LIBC.LIB或LIBCMT.LIB进行链接,将在__imp__func上发生LNK2001错误。如果不使用/MD选项编译,在使用MSVCxx.LIB链接时也会发生LNK2001错误。 (4)使用/ML选项编译时,如用LIBCMT.LIB链接会在_errno上发生LNK2001错误。 (5)当编译调试版的应用程序时,如果采用发行版模态库进行链接也会产生LNK2001错误;同样,使用调试版模态库链接发行版应用程序时也会产生相同的错误。 (6)不同版本的库和编译器的混合使用也能产生问题,因为新版的库里可能包含早先的版本没有的符号和说明。 (7)在不同的模块中使用内联和非内联的编译选项能够导致LNK2001错误。如果创建C++库时打开了函数内联(/Ob1或/Ob2),但是在描述该函数的相应头文件里却关闭了函数内联(没有inline关键字),只是将得到错误信息。为避免该问题的发生,应该在相应的头文件中用inline关键字标志为内联函数。 (8)不正确的/SUBSYSTEM或ENTRY设置也能导致LNK2001错误。 19. 如何调试一个没有源码的exe文件调用的dll在Visual C++ 6.0中,进入“Project Setting”对话框然后选择Debug标签页。通常Visual Studio默认“executable for debug session”为可执行文件名,但可以将他改成任何你想要的程序。甚至可以指定不同的工作目录以及传递参数到你的程序。这个技术常用来调试Dlls、名字空间扩展、COM对象和其他从某些EXE以及从第三方的EXE中调用的plug-in程序。 20. Visual C++ 6.0工程中的项目文件都表示什么.opt:工程关于开发环境的参数文件。如工具条位置等信息。 .aps(AppStudio File)资源辅助文件,二进制格式,一般不用去管它。 .clw:ClassWizard信息文件,实际上是INI文件格式,有兴趣可以研究一下。有时候ClassWizard出了问题,手工修改CLW文件可以解决。如果此文件不存在的话,每次用ClassWizard的时候回提示是否重建。 .dsp(DevelopStudio Project):项目文件,文本格式,不过不熟悉的不要手工修改。 .dsw(DevelopStudio Workspace):是工作区文件,其他特点和.dsp差不多。 .plg:是编译信息文件,编译时的error和warning信息文件(实际上是一个html文件),一般用处不大。在单击菜单【Tool】->【Option】弹出的对话框里面有个选项可以控制这个文件的生成。 .hpj(Help Project):是生成帮助文件的工程,用microsoft Help Compiler可以处理。 .mdp(Microsoft DevStudio Project):是旧版本的项目文件,如果要打开此文件的话,会提示你是否转换成新的.dsp格式。 .bsc:是用于浏览项目信息的,如果用Source Brower的话就必须有这个文件。如果不用这个功能的话,可以在Project Options里面去掉Generate Browse Info File,这样可以加快编译速度。 .map是执行文件的映象信息记录文件,除非对系统底层,这个文件一般用不着。 .pch(Pre-Compiled File):是与编译文件,可以加快编译速度,但是文件非常大。 .pdb(Program Database):记录了程序有关的一些数据和调试信息,在调试的时候可能有用。 .exp:只有在编译DLL的时候才会生成,记录了DLL文件的一些信息,一般也没有用。 .ncb:无编译浏览文件(no compile browser)。当自动完成功能出问题时可以删除此文件。编译工程后会自动生成。
2008年7月21日
一. 条件编译的使用 以下是该头文件用到的几个宏
  1 //每种环境都设置一个宏且都置0 2 #define PLAT_GTK 0 3 #define PLAT_GTK_WIN32 0 4 #define PLAT_MACOSX 0 5 #define PLAT_WIN 0 6 #define PLAT_WX 0 7 #define PLAT_FOX 0 8 //如果该环境有效则取消原有宏且置1 9 #if defined(FOX) 10 #undef PLAT_FOX 11 #define PLAT_FOX 1 12  13 #if defined(__WIN32__) || defined(_MSC_VER) 14 #undef PLAT_GTK_WIN32 15 #define PLAT_GTK_WIN32 1 16 #endif 17  18 #else 19 #undef PLAT_WIN 20 #define PLAT_WIN 1 21 22 #endif 23
如此设置各个宏后就可以使用了
 class Palette {
int used;
int size;
ColourPair *entries;
#if PLAT_GTK//1为真,这里可以是一个常量表达式
void *allocatedPalette; // GdkColor *
int allocatedLen;
#endif
 

如果是GTK环境则需要另外设置两个变量,一个是分配调色板,一个是指明长度。若为win32环境就无需这两个变量了。该条件编译用于生成不同平台下的产品。即编译代码的不同部分,这就是预处理的一个功能。
二.Typedef的使用
 
typedef void *FontID;
typedef void *SurfaceID;
typedef void *WindowID;
typedef void *MenuID;
typedef void *TickerID;
typedef void *Function;
typedef void *IdlerID;

不同平台的具体类型不同,可以用该方法定义新的名字,使程序更清晰。
三.关于颜色的管理的几个类
ColourDesired
颜色使用一个长整形,也就是用32位来描述一种颜色。
主要看两个Set函数的实现,第一个是从指定的三基色的值来构造一种颜色。
 void Set(unsigned int red, unsigned int green, unsigned int blue) {
co = red | (green << 8) | (blue << 16);
}

从代码看出从高到低依次是bgr,这个顺序正好是RGB模型。那么每个值占8位。总共是24位,最高的8位我想是alpha通道吧。
另一种是从一个数组构造,目前猜测是xpm格式描述的数组的部分。他首先有个提取十六进制真值的函数作为辅助,比较简单。
 static inline unsigned int ValueOfHex(const char ch) {
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else
return 0;
}

 void Set(const char *val) {
 if (*val == '#') {
val++;
}
unsigned int r = ValueOfHex(val[0]) * 16 + ValueOfHex(val[1]);
unsigned int g = ValueOfHex(val[2]) * 16 + ValueOfHex(val[3]);
unsigned int b = ValueOfHex(val[4]) * 16 + ValueOfHex(val[5]);
Set(r, g, b);
}

再看一个图标的数组
  1 /**//* XPM */ 2 static char * arrow_xpm[] = { 3 "12 12 3 1", 4 " c None", 5 ". c #000000", 6 "+ c #808080", 7 " ", 8 " .+ ", 9 " .+ ", 10 " +.+ ", 11 "  ..+ ", 12 "   + ", 13 "   + ", 14 "  ..+ ", 15 " +.+ ", 16 " .+ ", 17 " .+ ", 18 " "}; 19
可以看到该字符图像由三种符号组成,然后三种符号的颜色显然是这么描述的"
c None",
". c #000000",
"+ c #808080",
Set函数遇到一个#号就开始提取计算颜色rgb值了。那么整个图像应该是通过这个Set函数的几次调用进行的。ColourAllocated类简单的把上一个类求的值赋给他的成员。具体作用还不清楚。ColourPair类包含以上两个类的对象,这些可能是设计上的原因。因为具体的操作都是ColourDesired完成。
2008年7月17日
主要类
|
CellBuffer
|
保存文本,样式信息,恢复堆栈,标签,代码叠起结构等信息
|
|
ContractionState
|
|
|
Document
|
包含CellBuffer和一些高度抽象操作,管理样式处理。
|
|
Editor
|
使用ContractionState, Indicator, LineMarker, Style, ViewStyle来显示文档KeyMap和ContractionState同样在这里使用。
|
|
Indicator
|
|
|
LineMarker
|
|
|
Style
|
|
|
ViewStyle
|
|
|
KeyMap
|
|
|
ScintillaBase
|
Editor的子类,增加了调用提示和自动完成等功能,使用类CallTip和AutoComplete
|
|
CallTip
|
|
|
AutoComplete
|
|
Scintilla文档的每个字符都紧跟关联的样式信息。一个字节的字符信息和一个字节的样式信息作为一个单位。样式信息高3位是指示器,低5位是索引号。索引号索引一个存放样式的数组。这样就可以表示32种基础样式,几乎包含所有语言的样式。三个无关指示器可以一次显示语法错误,非法命名,和缩进错误。关于样式的位可以通过SCI_SETSTYLEBITS来改变最多其中7位,剩下的位用于指示器。
字符位置信息以0开始计数,至nLen-1,中文的字符是两个字符为一个文字,这样计数就有误了(DBCS)
Scintilla的消息都是以SCI_GETxxx或者SCI_SETxxx来命名的
一.文本取回与修改
主要消息有:
 
SCI_GETTEXT(int length, char *text)

SCI_SETTEXT(<unused>, const char *text)

SCI_SETSAVEPOINT

SCI_GETLINE(int line, char *text)

SCI_REPLACESEL(<unused>, const char*text)

SCI_SETREADONLY(bool readOnly)

SCI_GETREADONLY

SCI_GETTEXTRANGE(<unused>, TextRange*tr)

SCI_ALLOCATE(int bytes, <unused>)

SCI_ADDTEXT(int length, const char *s)

SCI_ADDSTYLEDTEXT(int length, cell *s)

SCI_APPENDTEXT(int length, const char*s)

SCI_INSERTTEXT(int pos, const char*text)

SCI_CLEARALL

SCI_CLEARDOCUMENTSTYLE

SCI_GETCHARAT(int position)

SCI_GETSTYLEAT(int position)

SCI_GETSTYLEDTEXT(<unused>, TextRange*tr)

SCI_SETSTYLEBITS(int bits)

SCI_GETSTYLEBITS

SCI_TARGETASUTF8(<unused>, char *s)

SCI_ENCODEDFROMUTF8(const char *utf8,char *encoded)

SCI_SETLENGTHFORENCODE(int bytes)


1)SCI_GETTEXT(int length, char *text)
此函数可以取得控件中的字符串存到一个缓冲区,这样就可以保存文档了。流程是使用SCI_GETLENGTH获得字符串的长度然后根据取得的长度申请一个缓冲区,再利用该消息取得文本,然后就可以保存文本了,同时需要利用SCI_SETSAVEPOINT标记文本已保存了。
做了例子果然是可以运行的,不知道作者是如何制作这样一个控件的。我对他的内部运行机制很感兴趣,想仔细看一些具体的代码,而不是仅仅使用它。另外发现API使用的字符是宽字符,这一点很令人讨厌。
另外获取处理函数的过程用了上次写的typedef的用法,改一下看上去就清晰多了
typedef int (*EditorSendFun)(void*,int,int,int);
void* ptr;
EditorSendFun editsendmessage;
2008年7月14日
2008年7月13日
最近看Scintillia的源代码,总看到typedef的身影,朋友也说autodesk的面试官曾说过不懂typedef很差劲。于是查了网上的资料,看了几种比较
容易出错的常用用法,做了一些整理。
一.起别名的两种用法
1. typedef (int *) pInt;

2. typedef pInt (int *)

比如pInt a,b;
第一种表示: int*a;int*b;
第二种表示: int*a,b;
所以第一种更像一个类型,第二种更像宏。
二.旧式代码中声明对象
typedef struct tagPoint

  {

Int x;

Int y;

}POINT;

POINT a,b;

三.代码简化
为复杂的声明定义一个新的简单的别名
方法:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版
typedef int (*PF) (const char *, const char *);
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。
如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
PF Register(PF pf);
Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。如果不用 typedef,那么代码是这样的:
int (*Register (int (*pf)(const char *, const char *)))(const char *, const char *);
摘自Scintilla文档
一.如何在窗口中建立Scintilla编辑控件
1. 载入动态链接库
1 hmod = LoadLibrary("SciLexer.DLL"); 2 if (hmod==NULL) 3  { 4 MessageBox(hwndParent, 5 "The Scintilla DLL could not be loaded.", 6 "Error loading Scintilla", 7 MB_OK | MB_ICONERROR); 8 } 9
2. 创建窗口(已经注册)
hwndScintilla = CreateWindowEx(0,
"Scintilla","", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN,
10,10,500,400,hwndParent,(HMENU)GuiID, hInstance,NULL);

二.如何控制窗口类控件
方法一:给控件发送消息和接受来自控件的响应
SendMessage(hwndScintilla,sci_command,wparam,lparam);
方法二:首先通过SCI_GETDIRECTFUNCTION 和 SCI_GETDIRECTPOINTER消息获取编辑控件回调函数的指针和第一个参数,接下来就可以直接使用编辑控件的消息处理函数了。
int (*fn)(void*,int,int,int);
void * ptr;
fn = (int (__cdecl *)(void *,int,int,int))SendMessage(hwndScintilla,SCI_GETDIRECTFUNCTION,0,0);
ptr = (void *)SendMessage(hwndScintilla,SCI_GETDIRECTPOINTER,0,0);

然后使用该回调函数:
fn(ptr,sci_command,wparam,lparam);
三.如何接受响应
只要在父窗口消息处理函数中对WM_NOTIFY消息做相应处理
NMHDR *lpnmhdr;
[ ]
case WM_NOTIFY:
lpnmhdr = (LPNMHDR) lParam;
if(lpnmhdr->hwndFrom==hwndScintilla)
  {
switch(lpnmhdr->code)
  {
case SCN_CHARADDED:
[…]
break;
}}
break;

整个过程没有任何问题,做好后是这个样子的Download。 此时你会发现该控件你没有发送任何消息就已经具备了一定的功能,有redo undo操作,还有复制黏贴。
2008年6月28日
2008年6月14日
两本需要看的书 C++程序设计语言 经常看看思考思考习题认真对待。 算法导论 需要琢磨,更需要创造性的应用。
一些需要玩玩的小程序 简单文本编辑器 先把界面搞出来再实现功能 计算器
2008年5月18日
 图0
 图1.1
 图1.2
 图2.1
 图2.2
图0是原图,经过一次小波变换后变成图1.1,逆变换回来后是图1.2 看图1.2发现有些像素与原图不同 然后对图1.2经过一次小波变换变成图2.1,逆变换回来时图2.2 图2.2与原图十分相似 算法并没有不同 为什么会有这样的现象? 大家帮帮我哈。 ps:小波变换是有损的
2008年4月23日
2008年4月22日
2008年3月15日
2008年1月22日
摘要: 由于屏幕不够显示,滚动条成为必备品。我们也习以为常了,如果没有滚动条,我们的电脑生活就没那么轻松了^_^。要添加滚动条,我们必须知道客户区的信息,客户区是不断变化的,但是变化是就就有WM_SIZE消息,此时的lparam的高字节保存高度,低字节保存宽度。获取办法如下
caseWM_SIZE: &nb... 阅读全文
|