`
tempsitegoogle
  • 浏览: 867625 次
文章分类
社区版块
存档分类
最新评论

编译选项简析及相关编码规范

 
阅读更多

编译选项简析及相关编码规范

环境:VS2005

作者:童磊(magictong)

一、基础

Debug和Release本身并没有什么本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项工作而已。因此,我们可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

Debug 版本:

/MDd /MLd 或 /MTd

调试版本的运行时库

/Od

关闭优化

/D "_DEBUG"

#define _DEBUG,打开编译调试开关

/ZI

创建Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译

/GZ

捕获内存错误(VS2005已经废去,用RTC代替)

/Gm

最小化重编译开关,减少编译时间

Release 版本:

/MD /ML 或 /MT

发布版本的运行时刻函数库

/O1 /O2或 /Ox

优化开关,使程序最小,最快或者全面优化

/D "NDEBUG"

关闭编译调试开关

/GF

常量字符串池开关,合并重复的字符串

1、ASSERT在Release版本中是不会被编译的,这种语句要转化成检测语句才有效果,这个地方曾经发生过crash,原因就是pos越界了。

ASSERT宏大概是这样定义的:

#ifdef _DEBUG

#define ASSERT(x) if( (x) == 0) report_assert_failure()

#else

#define ASSERT(x)

#endif

而assert本身在assert.h里面的定义是这样的:

#ifdef NDEBUG

#define assert(_Expression) ((void)0)

#else

……

2、DEBUG下会进行变量特别填充(开启/RTC1),这个可以在debug下直接调试查看内存。在调试内存错误的时候特别有用(是不是真的有用,谁用谁知道)。

0xCC:填充未初始化的栈变量。(烫烫烫烫烫烫烫烫烫烫)有木有!!!

0xCD:填充从堆中申请的内存。(经常见到的“屯屯屯屯屯屯屯屯屯”)

0xDD:(VC6)已经释放的内存。(“葺葺葺葺葺葺葺葺葺葺”)

0xFEEE:已经释放的内存。(“铪铪铪铪铪铪铪铪铪铪铪”)

0xFD:填充应用程序申请内存的前后的内存(前面4个字节,后面4个字节)。

3、尽量建立UNICODE工程。国际化,尤其是从VC6转换过来的工程最好是第一时间转换成UNICODE工程。

二、高级

/EH /EH{s|a}[c][-]

异常处理模型

/GR

启用运行时类型信息

/Zc

一致性

/RTC /RTCc /RTCu /RTCs

运行时检测

/Ob /Ob{0|1|2}

内联函数展开

/Oy

传说中的FPO,栈帧指针省略

1、 下面的代码如果关闭了GR选项,会有运行时错误,编译时会报4541警告:

warning C4541: 'typeid' used on polymorphic type 'Base' with /GR-; unpredictable behavior may result

class Base

{

public:

virtual void func() {}

};

class DClass : public Base

{

};

……

DClass* pd = new DClass;

Base* pb = pd;

cout << typeid( pb ).name() << endl;

cout << typeid( *pb ).name() << endl;

cout << typeid( pd ).name() << endl;

cout << typeid( *pd ).name() << endl;

……

2、/Zc

/Zc:forScope 强制for 循环变量的作用域

for (int i = 0; i < 10; ++i)

{

cout << i << endl;

}

i = 100;

/Zc:wchar_t 设置wchar_t为内置类型

3、/Ob 内联函数展开,后面分别跟0 1 2三个数字表示三种意思。

/Ob0 不进行内联;

/Ob1 只内联使用关键字inline标识的函数或者类声明中定义的函数;

/Ob2 包含Ob1选择的函数并智能选择一些可以内联的函数进行内联;

注意:对于这个编译选项,编译器将其和关键字inline视为建议的。不保证函数将被内联。而且不能强制编译器去内联特定的函数。(譬如虚函数就不可能内联)

4、/Oy FPO栈帧指针优化,默认是关闭的

5、/EH /EH{s|a} 异常处理模型

/EHs 同步异常处理模型

/EHa 异步异常处理模型

详细说明可以见:

http://blog.csdn.net/magictong/archive/2011/03/17/6256685.aspx

关于异常处理一个有意思的问题,看下面的一段代码(这是一个真实的例子):

vector<int> vecData;

try

{

vecData[100] = 12;

}

catch(...)

{

printf("Catched a exception/n");

}

在正常情况,这个异常是不会被catch的,即使设置了异步异常处理,也不会被catch。看起来似乎很奇怪。

跟踪一下代码流程,也许就不奇怪了……

VS2005里面微软对CRT的一些与安全相关的代码做了些改动,新的CRT版本在遇到参数异常时,把异常抛给了默认的调试器(默认是Dr.Watson),而不再通知应用程序设置的异常捕获函数。我们可以看一下代码的流程。

0040129D cmp dword ptr [ebp-1Ch],0

004012A1 jne main+2AFh (4012AFh)

004012A3 mov dword ptr [ebp-104h],0

004012AD jmp main+2BEh (4012BEh)

004012AF mov edx,dword ptr [ebp-18h]

004012B2 sub edx,dword ptr [ebp-1Ch]

004012B5 sar edx,2

004012B8 mov dword ptr [ebp-104h],edx

004012BE cmp dword ptr [ebp-104h],64h

004012C5 ja main+2CCh (4012CCh)

004012C7 call _invalid_parameter_noinfo (407384h)

004012CC mov eax,64h

004012D1 shl eax,2

004012D4 mov ecx,dword ptr [ebp-1Ch]

004012D7 mov dword ptr [eax+ecx],0Ch

_CRTIMP void __cdecl _invalid_parameter_noinfo(void)

{

_invalid_parameter(NULL, NULL, NULL, 0, 0);

}

_CRTIMP void __cdecl _invalid_parameter(

const wchar_t *pszExpression,

const wchar_t *pszFunction,

const wchar_t *pszFile,

unsigned int nLine,

uintptr_t pReserved

)

{

_invalid_parameter_handler pHandler = __pInvalidArgHandler;

pszExpression;

pszFunction;

pszFile;

pHandler = (_invalid_parameter_handler) _decode_pointer(pHandler);

if (pHandler != NULL)

{

pHandler(pszExpression, pszFunction, pszFile, nLine, pReserved);

return;

}

// No user handler is defined. Notify the debugger if attached.

_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_INVALIDPARAMETER);

_invoke_watson(pszExpression, pszFunction, pszFile, nLine, pReserved);

}

_CRTIMP void __cdecl _invoke_watson(

const wchar_t *pszExpression,

const wchar_t *pszFunction,

const wchar_t *pszFile,

unsigned int nLine,

uintptr_t pReserved

)

{

......

/* Make sure any filter already in place is deleted. */

SetUnhandledExceptionFilter(NULL);

ret = UnhandledExceptionFilter(&ExceptionPointers);

// if no handler found and no debugger previously attached

// the execution must stop into the debugger hook.

if (ret == EXCEPTION_CONTINUE_SEARCH && !wasDebuggerPresent) {

_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_INVALIDPARAMETER);

}

TerminateProcess(GetCurrentProcess(), STATUS_INVALID_PARAMETER);

}

/* Make sure any filter already in place is deleted. */

SetUnhandledExceptionFilter(NULL);

通过这一句干掉了我们设置的异常处理程序,然后就直接结束程序了……太暴力了,但是我们也是可以干预这个过程的,在函数_invalid_parameter里面有句:

pHandler = (_invalid_parameter_handler) _decode_pointer(pHandler);

if (pHandler != NULL)

{

pHandler(pszExpression, pszFunction, pszFile, nLine, pReserved);

return;

}

这里有个Handler貌似可以设置,确实是可以设置的:

void invalid_parameter_handler(

const wchar_t * expression,

const wchar_t * function,

const wchar_t * file,

unsigned int line,

uintptr_t pReserved)

{

}

_set_invalid_parameter_handler(invalid_parameter_handler);

调用之后就直接返回,你可以在invalid_parameter_handler函数里面做一些事情,然后退出程序,或者啥都不做,因为_invalid_parameter里面如果是设置了invalid_parameter_handler的情况,也调用了之后,就直接return了,因此异常还会存在,会被SEH捕获。

附录:

http://msdn.microsoft.com/zh-cn/library/y0zzbyt4(v=vs.80).aspx

链接器选项

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics