Jove's Blog

I love what I do

关于Debug和Release之本质区别的讨论本文主要包含如下内容:

  1. Debug 和 Release 编译方式的本质区别

  2. 哪些情况下 Release 版会出错

  3. 怎样“调试” Release 版的程序

一、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:

  2. 优化:这类错误主要有以下几种:

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

(2) volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。

(3) 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有:

  1. _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 控制。

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

(1) 初始化内存和变量。

(2) 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)

(3) 函数返回前检查栈指针,确认未被修改.

三、怎样“调试” Release 版的程序

  1. 前面已经提过,Debug 和 Release 只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改 Release 版的编译选项来小错误范围。如上所述,可以把 Release 的选项逐个改为与之相对的 Debug 选项,如 /MD 改为 /MDd、/O1 改为 /Od,或运行时间优化改为程序大小优化。注意,一次只改一个选项,看改哪个选项时错误消失,再对应该选项相关的错误,针对性地查找。这些选项在 ProjectSettings… 中都可以直接通过列表选取,通常不要手动修改。由于以上的分析已相当全面,这个方法是最有效的。

2.你也可以像 Debug 一样调试你的 Release 版,只要加入调试符号。在 Project/Settings… 中,选中 Settings for “Win32 Release”,选中 C/C++ 标签,Category 选General,Debug Info 选 Program Database。再在 Link 标签 Project options 最后加上 “/OPT:REF” (引号不要输)。

偶遇一个简单的题但是其中的算法值得我借鉴 正是最大公约数和最小公倍数的求法 直接上代码简单易懂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdio.h> 
int gcd(int m,int n)//最大公约数
{
int t;
if(m<n)
t = n,n = m,m = t;
if(n == 0)
return m;
else
return gcd(n,m%n);
}
int lcm (int m,int n)//最小公倍数
{
return m*n/gcd(m,n);
}
int main()
{
int m,n,i=0;
int a[20];
while(scanf("%d %d",&m,&n) == 2&&n!=0)
printf("%dn",lcm(m,n));
}


1
2
3
return gcd(n,m%n); 


看到个转变大小写的程序   自己也编写了个   熟悉下大小写之间的转换   总之切记小写a是97  大写A是65   数字0是48大概的应该差不多了

这个程序要求输入  字符串   直到@停止 输出转变后的字符串   代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

int main()
{
char s[70]={0};
int i=0;
while(s[i]=getchar(),s[i]!='@')
{
if(s[i]>='a'&&s;[i] <='z')
s[i]-=('a'-'A');
else if(s[i]>='A'&&s;[i]<='Z')
s[i]+=('a'-'A');
i++;
}
cout<<s<<endl;
return 0;
}

今天看到个文本输入程序  很有启发 加以修改成了一个打字游戏 可是我运行的时候总是无法正确识别键盘输入今天不早了改天有时间再修改先晒下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*C语言打字游戏
**键盘读入不正确
**2011-6-8
*/
#include<stdio.h>
#include<conio.h>
#include<windows.h>
#include<stdlib.h>
#include <time.h>
#include <process.h>
#include<string.h>

int pos;
int level=1000;
int count;
int num;
int score;
int Down;
int intst;
int isRun;
int isOver;
int isRight;
int isFull;
int map[11][11];

int Build_char()
{
int res;
res=rand()%2?(65+rand()%26):(97+rand()%26);
return res;
}

int Build_Pos()
{
int res;
res=rand()%10;
return res;
}

void Listener(PVOID pvoid) //监视输入的线程函数
{
char temp;
while(1)
{
while(!_kbhit())
temp=getchar();
if(temp==num)
{
score+=10;
isRight=1;
if(score==100)
{
isFull=1;
temp='0';
}
}
if(temp=='1')
isRun=1-isRun;
if(temp=='0')
{
isOver=1;
return;
}
}
}

void Repaint(PVOID pvoid)//重画
{
int i,j;
while(1)
{
if(isOver)break;
while(!isRun);
system("cls");
printf(" 时间:%d 得分:%d 1---暂停,0---退出n--------------------------------------------------------n",time(0)-intst,score);
for(i=0;i<=10;i++)
{
for(j=0;j<=10;j++)
if(map[i][j])
printf("%c",map[i][j]);
else
printf(" ");
printf("n");
}
printf("--------------------------------------------------------n");
}

}

void Calculate()
{
memset(map,0,sizeof(map));//全屏清零
map[count][pos]=num;
}

void Run()
{
int flag=1;
int cnt;
while(flag)//出现数字
{
cnt=Down;
count=1;
pos=Build_Pos();
num=Build_char();
while(cnt&&!isRight)//数字下掉
{
if(isOver)return;
while(!isRun);
Calculate();
count++;
cnt--;
Sleep(level);
}
isRight=0;
}
}

void init()
{
score=0;
isOver=0;
Down=10;
isFull=0;
isRun=1;
isRight=0;
intst=time(0);
memset(map,0,sizeof(map));
}

int main()
{
init();
_beginthread(Listener,0,NULL); //开线程
_beginthread(Repaint,0,NULL); //开线程
Run();
if(isFull)
printf("祝贺你,你赢了n");
//system("cls");
return 0;
}

今天没事想起了比赛时的编程第一题  就靠回忆又做了一遍

题目要求:输入一个5-20的数  输出一个边长为该数的螺旋三角例如输入4屏幕输出

1  2  3  4

9  10  5

8  6

7

思路就是把整个图形分为若干层逐层输入

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <iomanip>
using namespace std;
void main()
{
int n,m,i=1,j=1,u=1,N[20][20];
cin>>n;
for(m=1;m<=n-2;m++)
{
while(j<=n-2*m+2)
{
N[i][j]=u++;
j++;
}
j--;j--;i++;
while(i<=n-2*m+2)
{
N[i][j]=u++;
j--;
i++;
}
i--;i--;j++;
while(i>m)
{
N[i][j]=u++;
i--;
}
i++;
j++;
}

for(i=1;i<=n;i++)
{
for(j=1;j<=n-i+1;j++)
cout<<setw(3)<<N[i][j];
cout<<endl;
}

}