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

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

 

作者:童磊(magictong

 

环境:VS2005

一、基础

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

 

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

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

 

1ASSERTRelease版本中是不会被编译的,这种语句要转化成检测语句才有效果,这个地方曾经发生过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

……

 

2DEBUG下会进行变量特别填充(开启/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

链接器选项

 

参考文档:

1、  MSDN

2、  互联网

你可能感兴趣的:(优化,exception,filter,null,编译器,安全相关)