C语言程序调试Word格式.docx
《C语言程序调试Word格式.docx》由会员分享,可在线阅读,更多相关《C语言程序调试Word格式.docx(15页珍藏版)》请在冰豆网上搜索。
![C语言程序调试Word格式.docx](https://file1.bdocx.com/fileroot1/2022-11/28/2099396c-9f20-47b8-ad4b-9d22075866ff/2099396c-9f20-47b8-ad4b-9d22075866ff1.gif)
4,栈溢出(StackOverflows)
十:
轻松解决内存泄漏(HuntingMemoryLeaks)
结束语:
怎样尽可能的避免错误
**********************************************************/
下面就开始祥细讲解,/
深入了解Compile、Linking、Build
(1)Compile-编译
当您点击编译按钮时,编译器将会把你的源代码文件(.c文件)转换为目标文件(.obj文件),目标文件包含的是
源代码文件翻译后的机器语言。
这些是不能被直接运行的,还需要链接器将此中间代码与其他代码相结合来生成可执行文件。
请转看Linking,
Compile时,编译器通常会给你2种类型的提示:
warnings和errors
warnings
别小看warnings,它有可能会导致相当严重且极其隐蔽的bug,尤其是在指针管理内存这一块,/
常见的warning有以下几种类型
1,使用了未经初始化的变量,或者定义变量了却没有使用。
解析:
未经初始化的变量会存一个随机值,绝大多数的时候这个值都不是你想要的,你用它,编译器能不给你warning吗,?
2,使用了一些看上去非常愚蠢的语句,编译器都看不下去了
例如,if(blueguy=0)
printf("
blueguy=0!
!
"
);
if(blueguy&
&
greengirl||hemy)
;
3,使用了未定义的语句(注意,vc6.0是不会给这样的语句一个warning的)
例如,j=i+++i++;
//我自己都不知道自己想表达什么意思,呵呵
x=x>
0?
x++:
x--;
4,类型不匹配
例如,char*blueguy=(int*)greengirl;
本意是按单字节仿问内存的,结果却按四字节仿问内存,你感到崩溃,我感到崩溃,编译器也感到崩溃,估计编译器会真的崩溃了,/
5,函数原型明明写着有返回值的,结果函数体内却没有return一个值,反之亦然。
例如,
intmain(void)
{
}
或者
voidmain()
1
4
return0;
......等等,等等,等等。
/
好了,warnings就简单介绍到这里了,希望您写的程序里一个warning也没有
errors
出现errors时,相对来说比较好解决一些,通常编译器会给你明确的提示
像,"
syntaxerrors"
"
unexpectedparenthesis"
unexpectedendoffile"
之类的,
常见的errors有以下几种类型
(1)语句缺少"
;
号
for(;
)
structbluguy
intx;
(2)括号不匹配
if(!
blueguy
Compile就这样结束了,下面接着看Linking
(2)Linking-链接
vc6.0上是没有Linking按钮的,或许是我菜了,/没注意到
vc6.0的Build把Compile与Linking合在一起了,/
链接的作用是将目标代码、系统的标准启动代码和库代码结合在一起,生成可执行程序。
在你Compile的时候,编译器假定所有的结构体、函数、全局变量都已经在别的文件里声明了,但这个假设并不总是成立的,
链接器就是在文件中查看这些结构体、函数以及全局变量是不是已经声明了,/
常见的LinkerErrors有以下几种,
1,"
undefinedfunction"
-不明确的函数(这可能是函数参数不匹配或者未包含相应的库或者函数没有函数原型造成的,/)
2,"
couldnotfinddefinitionforX"
-
使用的变量未定义,
3,"
multipledefinitions"
-多重定义(多个文件定义了相同的函数或全局变量)
(3)Build-组建
Build没什么好讲的,就是集成了Compile与Linking的功能。
将Compile与Linking分隔开来,可以让你能够单独编译文件,总之是为了方便管理的,/
顺便说一下,当您的程序出现了,warning或者Errors的时候,双击一下提示信息就可以定位到那一行,/
断点
(1)普通断点,
图1
nomal.jpg(60.42KB)
普通断点是最简单,也是最常用的,只要在能够下断点的位置下上断点(按下F9,有的行是不能下断点的),上图中,断点下在blueguy=0;
这条语句上,也就是第9行,以下简称"
第9行"
,好,按下F5,程序立马就断在blueguy=0;
这条语句上。
断下来有什么用?
请跳转本文列表三:
(2)条件断点1-遇到断点一定次数后断下来
还是在图1的第9行下个断点,然后,按下Ctrl+b,弹出如下对话框
图2
condition1.jpg(32.56KB)
单击,at{"
blueguy.c"
}.9这一行后,弹出如下对话框
图3
condition2.jpg(39.05KB)
好,现在单击Condition按钮,弹出如下对话框
图4
condition3.jpg(50.53KB)
接下来,在stopping标识的文本框内填上一个你想要填上的数字,这里填的是6。
好,
单击OK按钮,再次按下F5,程序断在了第9行。
此时按下Ctrl+b可以看到
图5
condition4.jpg(34.93KB)
(2)条件断点2-某个变量(普通变量或指针变量)的值发生变化时断下来
还是在第9行下上断点,先按下F5,程序断在了第9行,按下Ctrl+b
弹出如下对话框
再按下Condition按钮,弹出如下对话框
condition3.jpg(36.24KB)
在Entertheexpressiontobeevaluated标识的文本框下
写上你想写入的变量,这里写的是blueguy,再按下F5,好,程序断在了第9行,并伴有下图提示
condition5.jpg(24.97KB)
(3)数据断点-某个表达式的值为真时断下来
单击下Date按钮,弹出如下对话框,
data1.jpg(41.11KB)
写上你想写的表达式,这里写的是greengirl==6
好,先按下F9撤消断点,再按下F5,程序断在了第9行,伴有下图提示:
见下楼,
data2.jpg(33.24KB)
如果你嫌麻烦的话,你也可以这样下断点
if(greengirl==6)
blueguy=0;
不过这样做,调试过后你得记得删除它,否则会给阅读代码选成视觉障碍,
好了,怎样下断点就讲到这里,如果你细心的话,会发现还有个Message按钮,那是消息断点,不怎么常用,
不在我的讲解之列,/
先上张大图
debug.jpg(146.13KB)
假设程序断在了第9行
这个窗口可以查看自动变量的值,不过他的最大用途在于查看函数的返回值。
假设有这样一个程序段
#include<
stdio.h>
intblueguy(void){return5;
intsum(inta,intb){returna+b;
%d"
sum(5,blueguy()));
如果,我想查看sum()以及blueguy()的返回值,如果不使用variables窗口,
我就得定义两个变量来接收sum()以及blueguy()的返回值,例似这样的语句,intgreengirl=blueugy()....是不是?
很幸运,variables窗口为我们省去了这个麻烦。
现在我们把断点下在return0;
按下F9,程序断下来。
看下面这张图,很惊喜吧,
Autos.jpg(59.43KB)
点击调试工具条上的watch按钮
1,现在想查看下greengirl的值,只需把光标放在greengirl上选中,拖到watch窗口里就行了
2,watch窗口中,在整形变量后面加上"
c"
可以显示该变量对应的ASCII字符,也可以直接敲数字这么显示,比如118,c的对应值是'
v'
'
d就是显示字符'
对应的十进制ASCII码值是118,'
x显示的是对应的十六进制的ASCII码值
3,数组名后加上"
N"
(N表示表示数组元素个数)可以查看该数组的值,对于查看大型数组的尾部数据比较方便
例如,intblueguy[100][10]={...};
watch窗口中,输入blueguy[99],10,可以看到二维数组最后一维的数据。
4,watch可以计算简单算式的值,例如:
blueguy-greengirl
点击调试工具条上的stack按钮
假设现在内存崩了,且崩在了第9行,stack显示了函数的调用关系,可以逐级向上检查错误
点击调试工具条上的memory按钮
memory窗口可以查看指针所指的内存区域里的值,使用时,把指针的值放在Address里就可以了,这在查看声音、图片、文件等大型数组的值时相当方便
(5)disassembly
有时内存崩了,弹出这个反汇编窗口,在这个窗口里可以看到相应的函数,不过用途不大。
还有两个不常用的窗口,不在我的讲解之列
就简单的介绍到这里了,反正这些都是很常用的,具体怎么用不在我的讲解之列
断言
使用断言时,先加上头文件,<
assert.h>
断言是在条件表达式不成立的情况下,终止程序。
断言是用来调错的,不要用来作为异常处理语句
假设现在内存崩了,并且程序中有如下代码
char*blueguy=malloc(10000);
我现在想看看是不是blueguy分配失败了,
可以这样写个断言
char*blueguy=malloc(blueguy);
assert(blueguy);
现在假设blueguy分配失败,为空,那么编译器就会弹出类似下图的提示框回复
assert.jpg(32.26KB)
这个窗口上面写着错误在哪一个文件,哪一行
,此时找到那个文件,按下ctrl+g,在框内填上行数就可以定位到那一行。
五:
printf()
现在讲解下深受控制台下编程的朋友钟爱的printf()函数;
但请注意,不要迷恋printf();
因为他的功能并不是那么强大,想查看某个值的时候,断个点就可以,不用花力气去书写printf()输出语句。
不过printf()也有它的优点,想查看变量之外的信息的时候,还是很有用的
比如说,我想看下汉诺塔递归程序的函数调用路径
可以定义一个监视变量表示递归深度,/
Example:
//声明:
本人不保证程序能够正确运行,
voidHanoi(intn,chara,charb,charc)
if(n==1)
{
while(n--)
"
Hanoi(%d,%c,%c,%c)\n"
n,a,b,c);
//cout<
<
a<
->
<
b<
endl;
//printf("
%c->
%c\n"
a,b);
return;
}
//先将n-1个盘子,以b为中转,从a柱移动到c
Hanoi(n-1,a,c,b);
//将一个盘子从a移动到b
//将c柱上的n-1个盘子,以a为中转,移动到b柱
Hanoi(n-1,c,b,a);
intN;
scanf("
&
N);
Hanoi(N,'
A'
'
B'
C'
当然,printf()还有其他的用途,关键看你怎么用了,不在我的讲解之列
有时候,前面介绍的调试技术(本文已经通过编程中国ISO9001认证)并不能直观的看到数据的变化,比如watch窗口太小了,放不下那么多的值...
Log是java/c#中的名词,c语言是没有提供这个调试功能的,实际上所谓的Log也没什么东西,无非是打开文件,写入数据罢了。
我们可以用c语言的fopen()、fwrite()等文件操作函数来仿写Log
这里要介绍两个用于调试的重要的宏
__FILE__
表示文件名
__LINE__
表示调用语句所在的行数
voidLog(void)
charbugInfo[200];
pFile=fopen("
E:
\\blueguyDebug.log"
"
a+"
);
sprintf(bugInfo,"
File:
%s
Line:
%d:
Hereisabug"
__FILE__,__LINE__);
fwrite(bugInfo,sizeof(bugInfo[0]),sizeof(bugInfo),pFile);
fclose(pFile);
/*一套完整的日志接口*/
voidbgOpenLog(void)
if(Log==NULL)
Log=fopen("
log.txt"
w"
voidbgWriteLog(constchar*s)
fwrite(s,sizeof(char),strlen(s),Log);
voidbgCloseLog(void)
fclose(Log);
Log=NULL;
不再多说了,继续看下一个调试技术
假设你现在在写windows应用程序,
程序出错了,你想用printf()函数来输出信息,很遗憾的告诉你,可能没有,
这个时候,您可以使用可变参数来仿写printf()函数
Trace(constchar*fmt,...)
chartext[256];
va_list
ap;
if(fmt==NULL)
return;
va_start(ap,fmt);
vsprintf(text,fmt,ap);
va_end(ap);
//在屏幕上画出这个字符串
虚拟内存简介(VirtualMemory)
每一个跑在你的操作系统的应用程序都有一个唯一的地址空间。
这些地址空间看起来是连续的内存块,实事上,它们并不是物理上连续的内存,仅仅是操作系统给应用程序的一个镜像空间--虚拟内存.
每个应用程序可利用的虚拟内存一般划分为六个段:
环境变量段--用于存储环境变量和命令行参数。
栈--用于存储函数参数,自动变量等。
堆--用于动态分配内存
两个数据段--分别用于存储初始化和未初始化的静态变量或全局变量
文本段--用于存放实际的代码
stdlib.h>
string.h>
char*pStr=(char*)malloc(512);
charc=pStr[0];
//thecontentsofpStrwerenotinitialized
char*pStr=(char*)malloc(25);
free(pStr);
strcpy(pStr,"
blueguy"
)//Invalidwritetodeallocatedmemoryinheap
char*pStr=(char*)malloc(20);
//resultsinaninvaliddeallocation
1,无效的内存仿问(InvalidMemoryAccess)
int*blueguy(void);
int*greengirl=NULL;
greengirl
=blueguy();
greengirl[0]);
int*blueguy(void)
inta[10]={1,2,3,4,5,6,7,8,9,10};
returna;
2,未初始化的内存仿问(UninitializedMemoryAccess)
inta;
intb=a*4;
//uninitializedreadofvariablea
//
intmain(voi