技巧一:调试release程序的一个小方法
我们经常会发现调试release版本的程序时编译器有时无法提供正确的数值。那么可以将信息输出至测试文本或者调试窗口进行查看:
e.g:输出到文件
CRect rect;
m_cParamPanel.GetWindowRect(rect);
FILE * pfile = NULL;
pfile = fopen("test.txt","w");
fprintf(pfile,"create pos:%d,%d,%d,%d/n",rect.top,rect.left,rect.bottom,rect.right);
fclose(pfile);
技巧二:动态库设置别名的问题
让一个动态链接库文件debug和release编译的时候生成不同的文件名的方法:
1、去掉DEF文件中的LIBRARY;
2、修改settings-->link-->output file name,改为希望的名字,比如debug的改为test_d.dll,release的不变。
1很重要,否则虽然生成了test_d.dll和test_d.lib,但是运行的时候会报告找不到动态链接库"test.dll",因为test_d.lib中使用的internal name是"test.dll",去掉DEF文件中的LIBRARY以后就没有这个问题了!
技巧三:OpenGL编程过程中的错误信息输出
opengl不会自动将一些错误信息输出。需要显示调用glGetError函数并通过返回的错误码将错误字符输出。在发现渲染异常时调用下面的代码来查找opengl的error,有助于纠正潜在的错误。这些错误包括矩阵或者属性栈的溢出、非法操作等。下面的函数可先在绘制render函数的最开始进行一次调用,让错误信息清零。每绘制部分场景再调用一次,查看是否存在绘制错误。
void OpenglErrorOutput()
{
GLenum errcode = glGetError();
if (GL_NO_ERROR!=errcode)
{
const GLubyte * errstring = gluErrorString(errcode);
char strerr[256];
sprintf(strerr,"%s/n",errstring);
OutputDebugStringA(strerr);
}
}
技巧四:过多地在堆(STACK)上分配内存将产生以下错误
First-chance exception at 0x7c80bef7 in ParaTree.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0012e940..
Unhandled exception at 0x7c80bef7 in ParaTree.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0012e940..
建议如果需执行多次的操作需用动态new,或者采用异常检测机制。捕捉内存分配失败的异常。
技巧五:遍历删除vector中满足条件的元素
以前使用VC6开发,最近换用VS2005编译以前的程序,在运行时出现断言,切回调试器,发现提示是
"vector iterators incompatible”
引发这个错误的代码如下:
//====================旧代码
VectorType::iterator it = someVector.begin();
for (; it != someVector.end();)
{
if (*it== value)
{
someVector.erase(it);
}
else
{
++it;
}
}
//====================
代码中,在erase操作后,没有修改it就继续循环,在与end()比较时,断言出现。
这里的主要问题是,vector可以用任意方法实现erase,不保证在erase一个元素后,后续的元素一定被移动到这个iterator所引用的位置(地址)。当然,这在几乎所有STL的实现中,都是对的,这也就是以前用VC6编译后运行没有问题的原因。但如果这里用的不是vector,而是list或是map,运行到这里,程序会毫不犹豫的崩溃。
正确的做法是这样的:
STL里所有的容器类的erase实现都会返回一个iterator,这个iterator指向了“当前删除元素的后继元素,或是end()”
因此,在遍历容器的所有元素过程中通过erase删除一个元素后,将erase的返回值赋给迭代变量:
//====================新代码
VectorType::iterator it = someVector.begin();
for (; it != someVector.end();)
{
if (*it== value)
{
it = someVector.erase(it);
}
else
{
++it;
}
}
//====================
P.S. 可以看出,VS2005带的STL增加了更多的调试特性以避免出现STL的一些错误,有条件的话最好用VS2005的STL。如果没有VS2005,也可以使用STLport,STLport在调试特性上也非常出色。
技巧六:将vs2005的程序转到2008上,出现了如下warning
Warning 1 Command line warning D9035 : option 'Wp64' has been deprecated and will be removed in a future release cl
原因:
vs2008不再建议使用/wp64检测64兼容问题,因为可以直接在32位OS上交叉编译为64位代码(vs2005也可以)。vs2008建议直接使用该方法检测64位兼容性问题。该选项被设置为“不推荐”有个原因是它会导致某些template库发生许多无效的warning。
解决办法:
property-->c/c++ -->Detect 64 bit Portability Issues设为No。
技巧七:MDI视图切换(或标签切换)时如果需要进行处理应在子框架的OnMDIActivate函数中进行
如果在视图类的OnActiveView中处理,一些操作如最小化、切换程序窗口都会触发。也不利于调试。
void CChildFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
{
CMyView *pActiveView = NULL;
if ( pActivateWnd )
{
pActiveView = (CMyView*)((CChildFrame*)pActivateWnd)->GetActiveView();
if ( pActiveview )
{
/*Do Something*/
}
}
}
技巧八:关于无法打开PCH预编译头文件
vs使用中经常出现无法打开预编译头文件的情况,解决这种情况的方法是:
将工程的预编译头文件指令指定为/Yc的.CPP文件(默认为StdAfx.cpp)重新编译一次即可再次生成.PCH文件。具体选项位置为:
项目属性->C/C++->Precompiled Headers->Create/Use Precompiled Header
设定为Create Precompiled Header(/Yc).编译StdAfx.cpp结束后修改为Use Precompiled Header (/Yu)即可
技巧九:国际化语言实时切换的实现:
方法1:采用VC纯资源DLL解决国际化问题
实现方法参考地址:http://blog.csdn.net/i_mimi/archive/2008/05/16/2452705.aspx
方法2:采用ini配置文件方式。在注册表内写语言文件地址进行查找的方式
实现方法可参考文章:‘VC下程序多语言化’电脑编程技巧与维护 2007年11期
技巧十:release状态下生成代码(Generating Code)非常慢的原因
之前写一个工程遇到release状态下生成代码需要一两个小时的情况。后来发现原来是工程中有一个cpp文件代码过长(几万行),取消该文件中函数的调用。link的速度恢复正常。应该算是低级失误。。。
技巧十一:调试着色器比较好的IDE:
1、AMD的RenderMonkey.可以用来调试OpenGL的GLSL和微软的HLSL着色语言,虽然从08年开始停止更新。但仍是调试GLSL的不二之选。不支持CG语言
2、nVidia的FX Composer,与微软联合出品。可以用来调试英威达自己维护的CG语言和微软的HLSL。是调试CG的不二之选
3、微软DX SDK里的PIX FOR Windows 可以用来调试HLSL。据说功能很强大。没用过。不予评论。
技巧十二:位运算的基础知识
系统程序中常要求在位(bit)一级进行运算或处理。C语言提供了位运算的功能,这使得C语言也能像汇编语言一样用来编写系统程序。
C语言提供了六种位运算符:
1、 & 按位与
2、| 按位或
3、 ^ 按位异或
4、~ 取反
5、<< 左移
6、>> 右移
1. 按位与运算 按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。
例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。
按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 ,保留低八位,可作a&255 运算 ( 255 的二进制数为0000000011111111)。
//=====
main(){
int a=9,b=5,c;
c=a&b;
printf("a=%d/nb=%d/nc=%d/n",a,b,c);
}
//=====
2. 按位或运算 按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。
例如:9|5可写算式如下: 00001001|00000101
00001101 (十进制为13)可见9|5=13
//=====
main(){
int a=9,b=5,c;
c=a|b;
printf("a=%d/nb=%d/nc=%d/n",a,b,c);
}
//=====
3. 按位异或运算 按位异或运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现,例如9^5可写成算式如下: 00001001^00000101 00001100 (十进制为12)
//=====
main(){
int a=9;
a=a^15;
printf("a=%d/n",a);
}
//=====
4. 求反运算 求反运算符~为单目运算符,具有右结合性。 其功能是对参与运算的数的各二进位按位求反。例如~9的运算为: ~(0000000000001001)结果为:1111111111110110
5. 左移运算 左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,
高位丢弃,低位补0。例如: a<<4 指把a的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。
6. 右移运算 右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。
例如:设 a=15,a>>2 表示把000001111右移为00000011(十进制3)。 应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时, 最高位补0,而为负数时,符号位为1,最高位是补0或是补1 取决于编译系统的规定。Turbo C和很多系统规定为补1。
技巧十三:OPENGL OSG DIRECTX坐标系
不论是OPENGL还是OSG,都采用是右手系,
opengl坐标系,即z轴正向朝外;y轴正向朝上;x轴正向朝右.
osg坐标系,即z轴正向朝上;y轴正向朝前;x轴正向朝右.
可以这么来说,将opengl坐标系沿x轴沿前下方旋转90度,即osg的默认坐标系.
这跟Directx是不一样的,Directx采用左手坐标系,即z轴正向朝里;y轴正向朝上;x轴正向朝右.
(未完待续)