精华内容
下载资源
问答
  • 二维数组正确初始化规则

    千次阅读 多人点赞 2019-03-19 20:30:50
    最近刷题总在二维数组初始化这里栽跟头,接下来总结一点二维数组的初始化规则以便记忆 这里提一句一维数字代表行,二维数字代表列 arr[2][3]就是创建两行三列的数组 二维数组在初始化的时候可以分行进行初始化 int...

    最近刷题总在二维数组初始化这里栽跟头,接下来总结一点二维数组的初始化规则以便记忆
    这里提一句一维数字代表行,二维数字代表列
    arr[2][3]就是创建两行三列的数组

    1. 二维数组在初始化的时候可以分行进行初始化
      int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };

    2. 二维数组也可以进行放在一起进行初始化
      int arr[2][3] = { 1, 2, 3, 4, 5, 6 };

    3. 二维数组在初始化的时候可以进行部分初始化
      int arr[2][3] = { { 1 }, { 2 } };
      这个初始化就和1, 0, 0, 2, 0, 0初始化一样(一般初始值为0)
      int arr[2][3] = { 1, 2, 3 };
      这个就是1, 2, 3, 0,0,0

    4. 二维数组的初始化可以省略第一维的数字,但是不能省略第二维的数字
      int arr[][3] = { { 1 }, { 2 }};
      这里可以看出也是用1, 0, 0, 2, 0, 0初始化
      int arr[][3] = { 1 ,2 ,3, 4 };
      这里用这种方式进行初始化的时候,一维的数字大小是由这个初始化数字的数量多少来取的,例如这个例子是4个数字那么就是4/3+1(如果有余数再加1)

    这里我们来练习一下

    在这里插入图片描述
    A选项省略了二维数字 错误
    C选项定义了2行数据,但实际初始化输入了3行 错误
    D选项不能使用{}定义

    在这里插入图片描述
    这里定义的数组是 1,0,0,0/ 3,2,0,0/ 4,5,6,0/ 0,0,0,0

    在这里插入图片描述
    A选项 1,0,2,0
    B选项 1,2 / 3,4
    C选项 1,0,2,3
    D选项 未对二维数字定义 错误

    展开全文
  • 正确使用goto语句

    千次阅读 2016-07-22 21:02:29
    问题起源: 60年代中期以后,计算机硬件技术日益进步,计算的存贮...然而软件技术的进步一直未能满足形势发展的需要,在大型软件的开发过程中出现了复杂程度高、研制周期长、正确性难以保证的三大难题。遇到的问题找
    问题起源:
    60年代中期以后,计算机硬件技术日益进步,计算的存贮容量、运算速度和可靠性明显提高,生产硬件的成本不断降低。计算机价格的下跌为它的广泛应用创造了极好的条件。在这种形势下,迫切要求计算机软件也能与之相适应。因而,一些开发大型软件系统的要求提了出来。然而软件技术的进步一直未能满足形势发展的需要,在大型软件的开发过程中出现了复杂程度高、研制周期长、正确性难以保证的三大难题。遇到的问题找不到解决办法,致使问题堆积起来,形成了人们难以控制的局面,出现了所谓的“ 软件危机”。为了克服这一危机,一方面需要对程序设计方法、程序的正确性和软件的可靠性等问题进行系列的研究;另一方面,也需要对软件的编制、测试、维护和管理的方法进行研究,从而产生了 程序设计方法学
    goto语句是有害的观点:
    1968年,Edsger Wybe Dijkstra 首先提出“GOTO语句是有害的”论点,向传统程序设计方法提出了挑战,从而引起了人们对程序设计方法讨论的普遍重视。
    goto语句的争论:
    在60年代末和70年代初,关于GOTO语句的用法的争论比较激烈。主张从高级程序语言中去掉GOTO语句的人认为,GOTO语句是对程序结构影响最大的一种有害的语句,他们的主要理由是:GOTO语句使程序的静态结构和动态结构不一致,从而使程序难以理解,难以查错。去掉GOTO语句后,可直接从程序结构上反映程序运行的过程。这样,不仅使程序结构清晰,便于理解,便于查错,而且也有利于程序的正确性证明。
    持反对意见的人认为,GOTO语句使用起来比较灵活,而且有些情形能提高程序的效率。若完全删去GOTO语句,有些情形反而会使程序过于复杂,增加一些不必要的计算量。
    关于goto语句的解决方法:
    1974年,D·E·克努斯对于GOTO语句争论作了全面公正的评述,其基本观点是:不加限制地使用GOTO语句,特别是使用往回跳的GOTO语句,会使程序结构难于理解,在这种情形,应尽量避免使用GOTO语句。但在另外一些情况下,为了提高程序的效率,同时又不至于破坏程序的良好结构,有控制地使用一些GOTO语句也是必要的。用他的话来说就是:“在有些情形,我主张删掉GOTO语句;在另外一些情形,则主张引进GOTO语句。”从此,使这场长达10年之久的争论得以平息。
    后来,G·加科皮尼和C·波姆从理论上证明了:任何程序都可以用顺序、分支和重复结构表示出来。这个结论表明,从高级程序语言中去掉GOTO语句并不影响高级程序语言的编程能力,而且编写的程序的结构更加清晰。
    goto语句的结果:
    在C/C++等高级编程语言中保留了goto语句,但被建议不用或少用。在一些更新的高级编程语言,如Java不提供goto语句,它虽然指定goto作为关键字,但不支持它的使 用,使程序简洁易读;尽管如此后来的c#还是支持goto语句的,goto语句一个好处就是可以保证程序存在唯一的出口,避免了过于庞大的if嵌套。
    二.可以考虑使用goto的情形
    1.从多重循环中直接跳出
    很多人建议废除C++/C的goto语句,以绝后患。但实事求是地说,错误是程序员自己造成的,不是goto的过错。goto 语句至少有一处可显神通,它能从多重循环体中一下子跳到外面,用不着写很多次的break语句。例如:
    for(......)
    {
    for(....)
    {
    for(.....)
    {
    // 如何冲出重重包围?
    }
    }
    }
    break;只能跳出单层的循环,return将整个函数都返回了,没法再继续了,显然也不行,所以我们想到了goto。如果是在陷入了很深层次的循环里想要跳出最外层的循环,用 goto 直接跳出却比用 break 一个循环一个循环地跳出要好得多。有人甚至形象比喻说:“就像楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。” 其实,你可以将 break 和 continue 理解成弱化了的 goto 语句。
    2. 出错时清除资源
    如果一个函数有多个出口,则在每个出口处,会产生巨大的退出代码,如下例一,每个函数只能有一个出口,所有的资源释放必须放在出口统一解决,那全部使用大括号,十几个,几十个if判断条件下来,你数数你的大括号有多深?这种代码可读性不好,一旦写错了,难于寻找错误。所有这些问题,一个goto就解决了。
    当程序要分配和清除资源时(像内存、或处理字形、窗口、打印机),这种情形下用goto通常是为了复制代码或清除资源。若遇到这种情况,程序员就要掂量是 goto 的缺点令人讨厌呢?还是复制代码那令人头痛的维护更讨厌呢?最后还是认为 goto 的缺点更可忍受。
    例子一:不用goto,想想需要申请的 指针是10个的话,程序怎么写?
    void Func(void)
    {
    char* p1=null;
    char* p2=null;
    char* p3=null;
    p1=(char*)malloc(10);
    if(!p1) return;
    p2=(char*)malloc(10);
    if(!p2)
    {
    free(p1);
    p1=null;
    return;
    }
    p3=(char*)malloc(10);
    if(!p3)
    {
    free(p1);
    p1=null;
    free(p2);
    p2=null;
    return;
    }
    ……
    ……
    …… //指针使用过程
    if(p1)
    {
    free(p1);
    p1=null;
    }
    if(p2)
    {
    free(p2);
    p2=null;
    }
    if(p3)
    {
    free(p3);
    p3=null;
    }
    }
    例子二:用goto
    void Func(void)
    {
    char* p1=null;
    char* p2=null;
    char* p3=null;
    p1=(char*)malloc(10);
    if(!p1) goto Func_End_Process;
    p2=(char*)malloc(10);
    if(!p2) goto Func_End_Process;
    p3=(char*)malloc(10);
    if(!p3) goto Func_End_Process;
    ……
    ……
    …… //指针使用过程
    Func_End_Process:
    if(p1)
    {
    free(p1);
    p1=null;
    }
    if(p2)
    {
    free(p2);
    p2=null;
    }
    if(p3)
    {
    free(p3);
    p3=null;
    }
    }
    3.可增加程序的清晰度的情况。
    若不使用goto语句会使功能模糊,有时候使用goto语句,一眼就看清楚了程序的意图,可用那些对应的循环break语句等实现的语句段,要想老半天才搞清楚程序意图的情况,也可考虑使用goto语句。
    三.不加限制地使用goto带来的弊端
    1).很明显,不加限制地使用goto破坏了清晰的程序结构,使程序的可读性变差,甚至成为不可维护的"面条代码"。例如下例:
    [code=C/C++]
    A: //code section A
    //code
    goto B;
    //code
    goto C;
    B: //code section B
    //code
    goto A;
    //code
    goto C;
    C: //code section C
    //code
    //goto B;
    //code
    goto A;
    [/code]
    这样好像已经能够说明问题了,随着标签的增多,带来的混乱局面是很难扭转的,对调试,走读,理解代码都会造成很大的障碍,如果你写这样的代码,那代码维护绝对会是一场 噩梦。
    2). 不加限制地使用goto经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句,例如:
    goto state;
    String s1, s2; // 被goto 跳过
    int sum = 0; // 被goto 跳过
    …..
    ……
    state:
    ……
    如果 编译器不能发觉此类错误,每用一次goto 语句都可能留下隐患。
    四.Goto语句与 结构化程序设计
    goto语句问题的提出直接推动了结构化程序设计(structured programming)的思想和 程序设计方法学的诞生和发展。 结构化程序设计方法引入了工程思想和结构化思想,使大型软件的开发和编程都得到了极大的改善。
    结构化程序设计方法的主要原则可以概括为自顶向下, 逐步求精,模块化,限制使用goto语句。
    1.自顶向下:程序设计时,应先考虑总体,后考虑细节;先考虑全局目标,后考虑局部目标。不要一开始就过多追求众多的细节,先从最上层总目标开始设计,逐步使问题具体化。
    2.逐步求精:对复杂问题,应设计一些子目标作为过渡,逐步细化。
    3.模块化:一个复杂问题,肯定是由若干稍简单的问题构成。模块化是把程序要解决的总目标分解为子目标,再进一步分解为具体的小目标,把每一个小目标称为一个模块。
    4.限制使用goto语句
    结构化程序设计方法的起源来自对goto语句的认识和争论。肯定的结论是,在块和进程的非正常出口处往往需要用goto语句,使用goto语句会使程序执行效率较高;在合成程序目标时,goto语句往往是有用的,如返回语句用goto。否定的结论是,goto语句是有害的,是造成程序混乱的祸根,程序的质量与goto语句的数量呈反比,应该在所有高级 程序设计语言中取消goto语句。取消goto语句后,程序易于理解、易于排错、容易维护,容易进行正确性证明。作为争论的结论,1974年Knuth发表了令人信服的总结,并证实了:
    (1)goto语句确实有害,应当尽量避免;
    (2)完全避免使用goto语句也并非是个明智的方法,有些地方使用goto语句,会使程序流程更清楚、效率更高。
    (3)争论的焦点不应该放在是否取消goto语句上,而应该放在用什么样的程序结构上。其中最关键的是,应在以提高程序清晰性为目标的 结构化方法中限制使用goto语句
    五.关于goto使用语句的一些建议
    goto语句在结构化编程技术出来后,被当作破坏 结构化程序的典型代表,可以说,在 结构化程序设计年代,goto语句就像洪水猛兽一样,程序员都唯恐避之不及;可后来在微软的一些例子程序中经常把goto语句用来处理出错,当出错时,goto到函数要退出的一个label那里进行资源释放等操作。那么,goto语句是不是只可以用于出错处理,其他地方都不可以用了呢?下列关于使用goto语句的原则可以供读者参考。
    1) 使用goto语句只能goto到同一函数内,而不能从一个函数里goto到另外一个函数里。
    2) 使用goto语句在同一函数内进行goto时,goto的起点应是函数内一段小功能的结束处,goto的目的label处应是函数内另外一段小功能的开始处。
    3) 不能从一段复杂的执行状态中的位置goto到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。
    4)应该避免向两个方向跳转。这样最容易导致"面条代码"。
    展开全文
  • 初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个. 人物创建的场景在Debug下正常,在Test和Release下正常,就是镜头不对。然后就盯着这个载入场景的配置文件的...

    原文:http://www.cppblog.com/lai3d/archive/2009/07/08/89514.html


    未初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个.

    人物创建的场景在Debug下正常,在Test和Release下不正常,就是镜头不对。然后就盯着这个载入场景的配置文件的代码看,ini不用了,换成xml看看,还是有问题!直接在代码里赋值,不用配置文件还是有问题!(之所以盯着这里看,是因为在Test下调试时发现读配置文件出来的值不对,但是用log打印出来却是对的!)
            于是否定了配置文件的问题,然后一想,既然是镜头的问题,就在setCamera(...)函数上打个断点不就行了,看哪里不该调用的时候调用了这个函数,Bingo!问题就找到了!丫的一未初始化的bool成员变量在Test下默认是true,我靠!
              出来混,迟早要还的!写了成员变量,立马初始化才是正道,千万别偷懒或者忘了初始化!

    另可参考:
    [转]DEBUG和RELEASE版本差异及调试相关问题

    Debug和Release有什么区别?怎么把Debug转成Release ?
    1。Debug和Release有什么区别,为什么要使用Release版本!  
    2。怎么把Debug转成Release  

    转载:  

    Debug版本包括调试信息,所以要比Release版本大很多(可能大数百K至数M)。至于是否需要DLL支持,主要看你采用的编译选项。如果是基于ATL的,则Debug和Release版本对DLL的要求差不多。如果采用的编译选项为使用MFC动态库,则需要MFC42D.DLL等库支持,而Release版本需要MFC42.DLL支持。Release   Build不对源代码进行调试,不考虑MFC的诊断宏,使用的是MFC   Release库,编译十对应用程序的速度进行优化,而Debug   Build则正好相反,它允许对源代码进行调试,可以定义和使用MFC的诊断宏,采用MFC   Debug库,对速度没有优化。    


    一、Debug   和   Release   编译方式的本质区别  

    Debug   通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release   称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。  
    Debug   和   Release   的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd   /Fo,但区别并不重要,通常他们也不会引起   Release   版错误,在此不讨论)  

    Debug   版本:  
    /MDd   /MLd   或   /MTd   使用   Debug   runtime   library(调试版本的运行时刻函数库)  
    /Od   关闭优化开关  
    /D   "_DEBUG"   相当于   #define   _DEBUG,打开编译调试代码开关(主要针对  
    assert函数)  
    /ZI   创建   Edit   and   continue(编辑继续)数据库,这样在调试过  
    程中如果修改了源代码不需重新编译  
    /GZ   可以帮助捕获内存错误  
    /Gm   打开最小化重链接开关,减少链接时间  

    Release   版本:    
    /MD   /ML   或   /MT   使用发布版本的运行时刻函数库  
    /O1   或   /O2   优化开关,使程序最小或最快  
    /D   "NDEBUG"   关闭条件编译调试代码开关(即不编译assert函数)  
    /GF   合并重复的字符串,并将字符串常量放到只读内存,防止  
    被修改  

    实际上,Debug   和   Release   并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。  

    二、哪些情况下   Release   版会出错  

    有了上面的介绍,我们再来逐个对照这些选项看看   Release   版错误是怎样产生的  

    1.   Runtime   Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的   Runtime   Library   包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的   Runtime   Library   通常很稳定,不会造成   Release   版错误;倒是由于   Debug   的   Runtime   Library   加强了对错误的检测,如堆内存分配,有时会出现   Debug   有错但   Release   正常的现象。应当指出的是,如果   Debug   有错,即使   Release   正常,程序肯定是有   Bug   的,只不过可能是   Release   版的某次运行没有表现出来而已。  

    2.   优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:  

    (1)   帧指针(Frame   Pointer)省略(简称   FPO   ):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误————但   Debug   方式下,栈的访问通过   EBP   寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release   方式下,优化会省略   EBP   栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。C++   的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在   Release   版本中强制加入   /Oy-   编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:  

    ●   MFC   消息响应函数书写错误。正确的应为  
    afx_msg   LRESULT   OnMessageOwn(WPARAM   wparam,   LPARAM   lparam);  
    ON_MESSAGE   宏包含强制类型转换。防止这种错误的方法之一是重定义   ON_MESSAGE   宏,把下列代码加到   stdafx.h   中(在#include   "afxwin.h"之后),函数原形错误时编译会报错  
    #undef   ON_MESSAGE  
    #define   ON_MESSAGE(message,   memberFxn)   \  
    {   message,   0,   0,   0,   AfxSig_lwl,   \  
    (AFX_PMSG)(AFX_PMSGW)(static_cast<   LRESULT   (AFX_MSG_CALL   \  
    CWnd::*)(WPARAM,   LPARAM)   >   (&memberFxn)   },  

    (2)   volatile   型变量:volatile   告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于   register   关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上   volatile   试试。  

    (3)   变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在   Debug   版中它有可能掩盖一个数组越界,而在   Release   版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有:  
    ●   非法访问,包括数组越界、指针错误等。例如  
    void   fn(void)  
    {  
    int   i;  
    i   =   1;  
    int   a[4];  
    {  
    int   j;  
    j   =   1;  
    }  
    a[-1]   =   1;//当然错误不会这么明显,例如下标是变量  
    a[4]   =   1;  
    }  
    j   虽然在数组越界时已出了作用域,但其空间并未收回,因而   i   和   j   就会掩盖越界。而   Release   版由于   i、j   并未其很大作用可能会被优化掉,从而使栈被破坏。  

    3.   _DEBUG   与   NDEBUG   :当定义了   _DEBUG   时,assert()   函数会被编译,而   NDEBUG   时不被编译。除此之外,VC++中还有一系列断言宏。这包括:  

    ANSI   C   断言   void   assert(int   expression   );  
    C   Runtime   Lib   断言   _ASSERT(   booleanExpression   );  
    _ASSERTE(   booleanExpression   );  
    MFC   断言   ASSERT(   booleanExpression   );  
    VERIFY(   booleanExpression   );  
    ASSERT_VALID(   pObject   );  
    ASSERT_KINDOF(   classname,   pobject   );  
    ATL   断言   ATLASSERT(   booleanExpression   );  
    此外,TRACE()   宏的编译也受   _DEBUG   控制。  

    所有这些断言都只在   Debug版中才被编译,而在   Release   版中被忽略。唯一的例外是   VERIFY()   。事实上,这些宏都是调用了   assert()   函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用   等),那么   Release   版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用   VC++   的   Find   in   Files   功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入   #ifdef   _DEBUG   之类的条件编译,也要注意一下。  
    顺便值得一提的是   VERIFY()   宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查   Windows   API   的返回值。有些人可能为这个原因而滥用   VERIFY()   ,事实上这是危险的,因为   VERIFY()   违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。  

    4.   /GZ   选项:这个选项会做以下这些事  

    (1)   初始化内存和变量。包括用   0xCC   初始化所有自动变量,0xCD   (   Cleared   Data   )   初始化堆中分配的内存(即动态分配的内存,例如   new   ),0xDD   (   Dead   Data   )   填充已被释放的堆内存(例如   delete   ),0xFD(   deFencde   Data   )   初始化受保护的内存(debug   版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且   32   位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在   Debug   版中发现   Release   版才会遇到的错误。要特别注意的是,很多人认为编译器会用   0   来初始化变量,这是错误的(而且这样很不利于查找错误)。  
    (2)   通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)  
    (3)   函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略   FPO   )  

    通常   /GZ   选项会造成   Debug   版出错而   Release   版正常的现象,因为   Release   版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。  

    除此之外,/Gm   /GF   等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。  
    --------------------------------------------------------------  
    Release是发行版本,比Debug版本有一些优化,文件比Debug文件小  
    Debug是调试版本,包括的程序信息更多  
    Release方法:  
    build->batch   build->build就OK.  

    -----------------------------------------------------  

    一、"Debug是调试版本,包括的程序信息更多"  

    补充:只有DEBUG版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息,所以体积小、运行速度快。  

    二、一般发布release的方法除了hzh_shat(水)   所说的之外,还可以project->Set   Active   Config,选中release版本。此后,按F5或F7编译所得的结果就是release版本。


    Trackback: http://tb.donews.net/TrackBack.aspx?PostId=131016

    ----------------------------------------------------------------------------------------
    -------------------------------------------------------------------------------------------
    VC下关于debug和release的不同的讨论
    在使用VC开发软件的过程中,正当要享受那种兴奋的时候突然发现:release与debug运行结果不一致,甚至出错,而release又不方便调试,真的是当头一棒啊,可是疼归疼,问题总要解决,下面将讲述一下我的几点经验,看看是不是其中之一:

    1. 变量。
    大家都知道,debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc(注1),而release的赋值近似于随机(我想是直接从内存中分配的,没有初始化过)。这样就明确了,如果你的程序中的某个变量没被初始化就被引用,就很有可能出现异常:用作控制变量将导致流程导向不一致;用作数组下标将会使程序崩溃;更加可能是造成其他变量的不准确而引起其他的错误。所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到,如debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了:( 还是自己多加注意吧

    2. 自定义消息的消息参数。
    MFC为我们提供了很好的消息机制,更增加了自定义消息,好处我就不用多说了。这也存在debug跟release的问题吗?答案是肯定的。在自定义消息的函数体声明时,时常会看到这样的写法:afx_msg LRESULT OnMessageOwn(); Debug情况下一般不会有任何问题,而当你在Release下且多线程或进程间使用了消息传递时就会导致无效句柄之类的错误。导致这个错误直接原因是消息体的参数没有添加,即应该写成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2)

    3. release模式下不出错,但debug模式下报错。
    这种情况下大多也是因为代码书写不正确引起的,查看MFC的源码,可以发现好多ASSERT的语句(断言),这个宏只是在debug模式下才有效,那么就清楚了,release版不报错是忽略了错误而不是没有错误,这可能存在很大的隐患,因为是Debug模式下,比较方便调试,好好的检查自己的代码,再此就不多说了。

    4. ASSERT, VERIFY, TRACE……….调试宏
    这种情况很容易解释。举个例子:请在VC下输入ASSERT然后选中按F12跳到宏定义的地方,这里你就能够发现Debug中ASSERT要执行AfxAssertFailedLine,而Release下的宏定义却为”#define ASSERT(f) ((void)0)”。所以注意在这些调试宏的语句不要用程序相关变量如i++写操作的语句。VERIFY是个例外,”#define VERIFY(f) ((void)(f))”,即执行,这里的作用就不多追究了,有兴趣可自己研究:)。

    总结:
    Debug与Release不同的问题在刚开始编写代码时会经常发生,99%是因为你的代码书写错误而导致的,所以不要动不动就说系统问题或编译器问题,努力找找自己的原因才是根本。我从前就常常遇到这情况,经历过一次次的教训后我就开始注意了,现在我所写过的代码我已经好久没遇到这种问题了。下面是几个避免的方面,即使没有这种问题也应注意一下:

    1. 注意变量的初始化,尤其是指针变量,数组变量的初始化(很大的情况下另作考虑了)。
    2. 自定义消息及其他声明的标准写法
    3. 使用调试宏时使用后最好注释掉
    4. 尽量使用try - catch(…)
    5. 尽量使用模块,不但表达清楚而且方便调试。
    注1:
    afc(afc) 网友提供:
    debug版初始化成0xcc是因为0xcc在x86下是一条int 3单步中断指令,这样程序如果跑飞了遇到0xcc就会停下来,这和单片机编程时一般将没用的代码空间填入jmp 0000语句是一样地
    注2:
    不知大家有没有遇到过这种情况,具体原因我也不太清楚,是不是调用时按着默认的参数多分配了WPARAM+LPARAM的空间而破坏了应用程序的内存空间?还请高手来补充。
    NightWolf 网友提供:我遇见过,好像是在函数调用的时候参数入栈的问题。因为MFC的消息使用宏写的,所以如果定义了OnMessage()的函数,编译能够通过,但是调用一次后,堆栈指针发生了偏移。然后就。。。
    ------------------------------------------------------------
    _________________________________________________________
    ---------------------------------------------------------




    [转]DEBUG和RELEASE版本差异及调试相关问题 
    I. 内存分配问题

    1. 变量未初始化。下面的程序在debug中运行的很好。

    thing * search(thing * something)
    BOOL found;
    for(int i = 0; i < whatever.GetSize(); i++)
    {
    if(whatever[i]->field == something->field)
    { /* found it */
    found = TRUE;
    break;
    } /* found it */
    }
    if(found)
    return whatever[i];
    else
    return NULL;
    而在release中却不行,因为debug中会自动给变量初始化found=FALSE,而在release版中则不会。所以尽可能的给变量、类或结构初始化。

    2. 数据溢出的问题

    如:char buffer[10];
    int counter;

    lstrcpy(buffer, "abcdefghik");

    在debug版中buffer的NULL覆盖了counter的高位,但是除非counter>16M,什么问题也没有。但是在release版中,counter可能被放在寄存器中,这样NULL就覆盖了buffer下面的空间,可能就是函数的返回地址,这将导致ACCESS ERROR。

    3. DEBUG版和RELEASE版的内存分配方式是不同的 。如果你在DEBUG版中申请 ele 为 6*sizeof(DWORD)=24bytes,实际上分配给你的是32bytes(debug版以32bytes为单位分配), 而在release版,分配给你的就是24bytes(release版以8bytes为单位),所以在debug版中如果你写ele[6],可能不会有什么问题,而在release版中,就有ACCESS VIOLATE。

    II. ASSERT和VERIFY

    1. ASSERT在Release版本中是不会被编译的。

    ASSERT宏是这样定义的

    #ifdef _DEBUG
    #define ASSERT(x) if( (x) == 0) report_assert_failure()
    #else
    #define ASSERT(x)
    #endif
    实际上复杂一些,但无关紧要。假如你在这些语句中加了程序中必须要有的代码
    比如

    ASSERT(pNewObj = new CMyClass);

    pNewObj->MyFunction();

    这种时候Release版本中的pNewObj不会分配到空间

    所以执行到下一个语句的时候程序会报该程序执行了非法操作的错误。这时可以用VERIFY :

    #ifdef _DEBUG
    #define VERIFY(x) if( (x) == 0) report_assert_failure()
    #else
    #define VERIFY(x) (x)
    #endif
    这样的话,代码在release版中就可以执行了。

    III. 参数问题:

    自定义消息的处理函数,必须定义如下:

    afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);

    返回值必须是HRESULT型,否则Debug会过,而Release出错

    IV. 内存分配

    保证数据创建和清除的统一性:如果一个DLL提供一个能够创建数据的函数,那么这个DLL同时应该提供一个函数销毁这些数据。数据的创建和清除应该在同一个层次上。

    V. DLL的灾难

    人们将不同版本DLL混合造成的不一致性形象的称为 “动态连接库的地狱“(DLL Hell) ,甚至微软自己也这么说http://msdn.microsoft.com/library/techart/dlldanger1.htm)。

    如果你的程序使用你自己的DLL时请注意:

    1. 不能将debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。

    解决办法是将debug和release的程序分别放在主程序的debug和release目录下


    2. 千万不要以为静态连接库会解决问题,那只会使情况更糟糕。

    VI. RELEASE板中的调试 :

    1. 将ASSERT() 改为 VERIFY() 。找出定义在"#ifdef _DEBUG"中的代码,如果在RELEASE版本中需要这些代码请将他们移到定义外。查找TRACE(...)中代码,因为这些代码在RELEASE中也不被编译。 请认真检查那些在RELEASE中需要的代码是否并没有被便宜。

    2. 变量的初始化所带来的不同,在不同的系统,或是在DEBUG/RELEASE版本间都存在这样的差异,所以请对变量进行初始化。

    3. 是否在编译时已经有了警告?请将警告级别设置为3或4,然后保证在编译时没有警告出现.

    VII. 将Project Settings" 中 "C++/C " 项目下优化选项改为Disbale(Debug)。编译器的优化可能导致许多意想不到的错误,请参http://www.pgh.net/~newcomer/debug_release.htm

    1. 此外对RELEASE版本的软件也可以进行调试,请做如下改动:

    在"Project Settings" 中 "C++/C " 项目下设置 "category" 为 "General" 并且将"Debug Info"设置为 "Program Database"。

    在"Link"项目下选中"Generate Debug Info"检查框。

    "Rebuild All"

    如此做法会产生的一些限制:

    无法获得在MFC DLL中的变量的值。

    必须对该软件所使用的所有DLL工程都进行改动。

    另:

    MS BUG:MS的一份技术文档中表明,在VC5中对于DLL的"Maximize Speed"优化选项并未被完全支持,因此这将会引起内存错误并导致程序崩溃。

    2. http://www.sysinternals.com/有一个程序DebugView,用来捕捉OutputDebugString的输出,运行起来后(估计是自设为system debugger)就可以观看所有程序的OutputDebugString的输出。此后,你可以脱离VC来运行你的程序并观看调试信息。

    3. 有一个叫Gimpel Lint的静态代码检查工具,据说比较好用http://www.gimpel.com/ 不过要化$的。

    参考文献:

    1) http://www.cygnus-software.com/papers/release_debugging.html

    2) http://www.pgh.net/~newcomer/debug_release.htm

    展开全文
  • 成员初始化

    千次阅读 2014-07-29 16:46:03
    成员初始化表#include class Account {public:Account();Account( const char*, double=0.0 );Account( const string&, double=0.0 );... //注意:构造函数的初始化列表只在构造函数的定义中指定,而在声

    成员初始化表

    #include <string>

    class Account {

    public:

    Account();

    Account( const char*, double=0.0 );

    Account( const string&, double=0.0 );

    Account( const Account& );

    // ...

    private:

    // ...

    };

     

    //注意:构造函数的初始化列表只在构造函数的定义中指定,而不在声明中指定

    inline Account::Account( const char* name, double opening_bal )

    : _name( name ), _balance( opening_bal )

    {

    _acct_nmbr = get_unique_acct_nmbr();

    }

    成员初始化列表跟在构造函数的原型后,以冒号开头。成员名是被指定的,后面是括在括号中的初始值,类似于函数调用的语法。如果成员是类对象,则初始值变成被传递给适当的构造函数的实参,该构造函数然后被应用在成员类对象上。在我们的例子中,name被传递给应用在_name上的string构造函数,_balance 用参数opening_bal 初始化。类似地,下面是另一个双参数Account构造函数:

    inline Account::Account( const string& name, double opening_bal )

    : _name( name ), _balance( opening_bal )

    {

    _acct_nmbr = get_unique_acct_nmbr();

    }

    在这种情况下,string的拷贝构造函数被调用,把成员类对象_name 初始化成string 参数name

    C++新手关注的一个常见问题是,使用初始化列表和在构造函数内使用数据成员的赋值之间有什么区别。例如,以下代码:

    inline Account::Account( const char *name, double opening_bal )

    : _name( name ), _balance( opening_bal )

    {

    _acct_nmbr = get_unique_acct_nmbr();

    }

    inline Account::Account( const char *name, double opening_bal )

    {

    _name = name;

    _balance = opening_bal;

    _acct_nmbr = get_unique_acct_nmbr();

    }

    它们的区别是什么?

    两种实现的最终结果是一样的。在两个构造函数调用的结束处,三个成员都含有相同的值,区别是成员初始化表只提供该类数据成员的初始化。在构造函数体内对数据成员设置值是一个赋值操作。区别的重要性取决于数据成员的类型。

    在构造函数初始化列表中没有显示提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中,它们被初始化为0(《c++ primer4th p388)。

    在概念上,我们可以认为构造函数的执行过程被分成两个阶段:隐式或显式初始化阶段,以及一般的计算阶段:

    初始化阶段可以是显式的或隐式的,取决于是否存在成员初始化表。隐式初始化阶段按照声明的顺序依次调用所有基类的缺省构造函数,然后是所有成员类对象的缺省构造函数。

    计算阶段由构造函数体内的所有语句构成。在计算阶段中,数据成员的设置被认为是赋值,而不是初始化。没有清楚地认识到这个区别是程序错误和低效的常见源泉。

    例如,当我们写如下代码:

    inline Account::Account()

    {

    _name = "";

    _balance = 0.0;

    _acct_nmbr = 0;

    }

    则初始化阶段是隐式的,在构造函数体被执行之前,先调用与_name相关联的缺省string构造函数,这意味着把空串赋给_name的赋值操作是没有必要的。

    对于类对象,在初始化和赋值之间的区别是巨大的。成员类对象应该总是在成员初始化表中被初始化,而不是在构造函数体内被赋值。缺省Account构造函数的更正确的实现如下:

    inline Account::Account() : _name( string() )

    {

    _balance = 0.0;

    _acct_nmbr = 0;

    }

    它之所以更正确,是因为我们已经去掉了在构造函数体内不必要的对_name的赋值。但是,对于缺省构造函数的显式调用也是不必要的,下面是更紧凑但却等价的实现:

    inline Account::Account()

    {

    _balance = 0.0;

    _acct_nmbr = 0;

    }

     

    剩下的问题是,对于两个被声明为内置类型的数据成员,其初始化情况如何?例如,用成员初始化表和在构造函数体内初始化_balance 是否等价?回答是不。对于非类数据成员的初始化或赋值,除了两个例外,两者在结果和性能上都是等价的。更受欢迎的实现是用成员切始化表:

    // 更受欢迎的初始化风格

    inline Account::Account() : _balanae( 0.0 ), _acct_nmbr( 0 )

    { }

    两个例外是指任何类型的const 和引用数据成员。const 和引用数据成员也必须是在成员初始化表中被初始化,否则,就会产生编译时刻错误。例如,下列构造函数的实现将导致编译时刻错误:

    class ConstRef {

    public:

    ConstRef( int ii );

    private:

    int i;

    const int ci;

    int &ri;

    };

    ConstRef::ConstRef( int ii )

    { // 赋值

    i = ii;   // ok

    ci = ii; // 错误: 不能给一个 const 赋值

    ri = i;   // 错误 ri 没有被初始化

    }

    当构造函数体开始执行时,所有const 和引用的初始化必须都已经发生。因此,只有将它们在成员初始化表中指定这才有可能。正确的实现如下:

    // ok: 初始化引用和 const

    ConstRef::ConstRef( int ii ):ci( ii ), ri( i )

    { i = ii; }

    每个成员在成员初始化表中只能出现一次,初始化的顺序不是由名字在初始化表中的顺序决定,而是由成员在类中被声明的顺序决定的。例如,给出下面的Account 数据成员的声明顺序:

    class Account {

    public:

    // ...

    private:

    unsigned int _acct_nmbr;

    double _balance;

    string _name;

    };

    下面的缺省构造函数:

    inline Account::

    Account() : _name( string() ), _balance( 0.0 ), _acct_nmbr( 0 )

    {}

    的初始化顺序为_acct_nmbr_balance然后是_name但是在初始化表中出现(或者在被隐式初始化的成员类对象中)的成员,总是在构造函数体内成员的赋值之前被初始化。例如,在下面的构造函数中:

    inline Account::Account( const char *name, double bal )

    : _name( name ), _balance( bal )

    {

    _acct_nmbr = get_unique_acct_nmbr();

    }

    初始化的顺序是_balance_name然后是_acct_nmbr

    由于这种“实际的初始化顺序”与“初始化表内的顺序”之间的明显不一致,有可能导致以下难于发现的错误,当用一个类成员初始化另一个时:

    class X {

    int i;

    int j;

    public:

    // ! 你看到问题了吗?

    X( int val ): j( val ), i( j )

    {}

    // ...

    };

    尽管看起来j 好像是用val 初始化的,而且发生在它被用来初始化i之前,但实际上是i先被初始化的,因此它是用一个还没有被初始化的j 初始化的。我们的建议是,把“用一个成员对另一个成员进行初始化(如果你真的认为有必要)”的代码放到构造函数体内。

     

    补充:

    为了让你的程序能够顺利编译,在下面4种情况下,必须使用member initialization list

    当初始化一个reference member时;

    当初始化一个const member时;

    当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对它们的赋值,这样是不被允许的。

    当调用一个base classconstructor,而它拥有一组参数时;

    当调用一个member classconstructor,而它拥有一组参数时;

    我们知道类的对象的初始化其实就是调用它的构造函数完成,如果没有写构造函数,编译器会为你默认生成一个。如果你自定义了带参数的构造函数,那么编译器将不生成默认构造函数。这样这个类的对象的初始化必须有参数。如果这样的类的对象来做另外某个类的成员,那么为了初始化这个成员,你必须为这个类的对象的构造函数传递一个参数。同样,如果你在包含它的这个类的构造函数里用“=,其实是为这个对象“赋值”而非“初始化”它。所以一个类里的所有构造函数都是有参数的,那么这样的类如果做为别的类的成员变量,你必须显式的初始化它,你也只能通过成员初始化列表来完成初始化。

     

    参考材料:

    c++ primer3th4th

    《深度探索C++对象模型》

    展开全文
  • 在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双 重检查锁定是常见的延迟初始化技术,但它是一个错误的用法。本文将分析双重检查锁定的 错误根源,以及两种线程安全的延迟初始化...
  • 下列有关静态成员函数的描述中,正确的是: A、静态数据成员可以在类体内初始化 B、静态数据成员可以被类对象调用 C、静态数据成员受private控制符作用 D、静态数据成员可以直接用类名调用答案:DA,静态...
  • C99结构体指定初始化

    千次阅读 2008-07-11 22:12:00
    在阅读GNU/Linux内核代码时,我 们会遇到一种特殊的结构初始化方式。该方式是某些C教材(如谭二版、K&R二版)中没有介绍过的。这种方式称为指定初始化(designated initializer)。下面我们看一个例子,Linux-2.6.x/...
  • 二维数组的初始化

    千次阅读 2011-07-23 16:03:16
    二维数组的初始化 二维数组初始化的形式为: 数据类型 数组名[整常量表达式][ 整常量表达式]={ 初始化数据 }; 在{ }中给出各数组元素的初值,各初值之间用逗号分开。把{ }中的初值依次赋给各数组元素。
  • 二维数组初始化的形式有?

    千次阅读 2017-07-14 16:30:51
    二维数组初始化的形式有?
  • 双重检查锁定与延迟初始化

    千次阅读 2014-11-14 13:49:39
    本来是看到多线程中关于...本文转自双重检查锁定与延迟初始化 ifeve.com 主要是介绍并发相关内容的网站,有自己原创内容,也有翻译外文,很给力。 双重检查锁定的由来 在java程序中,有时候可能需要推迟一
  • DirectX11 With Windows SDK--01 DirectX11初始化

    千次阅读 多人点赞 2018-05-12 18:20:37
    由于个人觉得龙书里面第4章提供的Direct3D 初始化项目封装得比较好,而且Direct SDK Samples里面的初始化程序过于精简,适合后续使用,故选择了结合两者的代码,并做进一部简化处理。 项目源码点此 链接...
  • 构造函数和初始化

    千次阅读 2009-10-15 12:19:00
    构造函数和初始化表 #include class Account { public: Account(); Account( const char*, double=0.0 ); Account( const string&, double=0.0 ); Account( cons
  • ORB-SLAM2详解(三)自动地图初始化

    千次阅读 2017-07-03 19:44:38
    ORB-SLAM2详解(三)自动地图初始化  欢迎转载,转载请注明网址http://blog.csdn.net/u010128736/   系统的第一步是初始化,ORB_SLAM使用的是一种自动初始化方法。这里同时计算两个模型:用于...
  • 莫偷懒!成员变量一定要初始化!

    千次阅读 2014-04-22 14:35:01
    成员变量一定要初始化! 未初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个. 人物创建的场景在Debug下正常,在Test和Release下正常,就是镜头不对。然后就盯着...
  • 控制全局变量初始化顺序 转自:http://nathan.xuli.googlepages.com/cpp_global   全局变量的初始化分两种。一种是static initialization,用常量来初始化,在程序被load的时侯就完成了。另一种就是dy
  • 当你在SQL Server上试图更新一个索引视图引用的表时,你可能回收到如下有错误INSERT 失败,因为下列 SET 选项的设置不正确: ARITHABORT在计算列或索引视图上创建或操作索引时,SET ANSI_NULLS 也必须为 ON。...
  • 原文:http://www.cppblog.com/lai3d/archive/2009/07/08/89514.html未初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个.人物创建的场景在Debug下正常,在Test和...
  • oracle10g初始化参数说明

    千次阅读 2006-11-14 09:52:00
    oracle10g初始化参数说明参数名: O7_DICTIONARY_ACCESSIBILITY类别:安全性和审计说明: 主要用于从 Oracle7 移植到 Oracle8i。如果该值为 TRUE, SYSTEM 权限 (如SELECT ANY TABLE) 将限制对 SYS 方案中各对象的...
  • 二维字符串数组的初始化-动态内存分配 http://blog.csdn.net/gangwazi0525/article/details/5960839  昨天在用FBS200 指纹采集芯片采集到一个二维数组数据后,利用串口传输上来的数据是以十六进制的数据格式...
  • 在阅读GNU/Linux内核代码时,我们会遇到一种特殊的结构初始化方式。该方式是某些C教材(如谭二版、K&R二版)中没有介绍过的。这种方式称为指定初始化(designated initializer)。下面我们看一个例子,Linux-2.6.x/...
  • Debug 与 Release 版本 变量 初始化(zz)

    千次阅读 2013-07-16 19:12:35
    //z 2013-07-16 19:09:30 IS2120@BG57IV3.T40682136 .K[T83,L895,R28,V1279] Surviving the Release Version By Joseph M. Newcomer, 16 Jul 2001 Introduction ...OK, your program works....
  • SQL命令和常用语句大全

    千次阅读 2016-05-22 21:41:32
    学习SQL应知道的动态SQL语句基本语法 1 、普通SQL语句可以用Exec执行 9Kp=A ' CdaFr1 eg: Select * from tableName Wsc+A: Exec('select * from tableName') #Btn( Exec sp_executesql N'select * from ...
  • continue和break分别在while语句,for语句和do while语句中的运行
  • SQL常用语句总结

    千次阅读 2012-08-29 09:18:50
    3.delete语句不影响表所占用的extent, 高水线(high w2atermark)保持原位置不动   显然drop语句将表所占用的空间全部释放   truncate 语句缺省情况下将空间释放到 minextents个 extent,除非使用reuse storage...
  • 经典SQL语句大全

    万次阅读 多人点赞 2019-05-10 10:01:53
    经典SQL语句大全 来源::https://www.cnblogs.com/1234abcd/p/5530314.html ** 一、基础 ** 1、说明:创建数据库 CREATE DATABASE database-name 2、说明:删除数据库 drop database dbname 3、说明:备份...
  • 3、全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。  4、文字...
  • for循环语句头的执行顺序

    万次阅读 2011-01-24 15:40:00
    第一次先初始化,然后判断,i++就执行了2、第一次完了以后,初始化这句就不用了,是先执行i++还是限制性判断语句呢?先执行i++3、现在知道是先执行i++,再执行判断语句,i++不是后++吗,例如i为1,那i++后执行判断...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,785
精华内容 17,514
关键字:

下列不正确的初始化语句是