c程序调试方法以及常见错误.docx

上传人:b****7 文档编号:23532852 上传时间:2023-05-18 格式:DOCX 页数:11 大小:23.02KB
下载 相关 举报
c程序调试方法以及常见错误.docx_第1页
第1页 / 共11页
c程序调试方法以及常见错误.docx_第2页
第2页 / 共11页
c程序调试方法以及常见错误.docx_第3页
第3页 / 共11页
c程序调试方法以及常见错误.docx_第4页
第4页 / 共11页
c程序调试方法以及常见错误.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

c程序调试方法以及常见错误.docx

《c程序调试方法以及常见错误.docx》由会员分享,可在线阅读,更多相关《c程序调试方法以及常见错误.docx(11页珍藏版)》请在冰豆网上搜索。

c程序调试方法以及常见错误.docx

c程序调试方法以及常见错误

使用VC6.0对C语言程序进行调试的基本手段

(1)设置固定断点或临时断点

所谓断点,是指定程序中的某一行,让程序运行至该行后暂停运行,使得程序员可以观察分析程序的运行过程中的情况。

这些情况一般包括:

①在变量窗口(Varibles)中观察程序中变量的当前值。

程序员观察这些值的目的是与预期值对比,若与预期值不一致,则此断点前运行的程序肯定在某个地方有问题,以此可缩小故障范围。

例如以下程序是计算cos(x)并显示,运行时发现无论x输入为多少,结果都是0.046414。

#include

#include

voidmain()

{

int x;

printf("Pleaseinputx:

");

scanf("%d",&x);

printf("cos(x)=%f\n",cos(x));

}

在该程序中,若你没问题——程序较长、较复杂时很难看出问题所在,则应该使用调试手段定位故障位置。

②在监控窗口(Watch)中观察指定变量或表达式的值。

当变量较多时,使用Varibles窗口可能不太方便,使用Watch窗口则可以有目的、有计划地观察关键变量的变化。

③在输出窗口中观察程序当前的输出与预期是否一致。

同样地,若不一致,则此断点前运行的程序肯定在某个地方有问题。

④在内存窗口(Memory)中观察内存中数据的变化。

在该窗口中能直接查询和修改任意地址的数据。

对初学者来说,通过它能更深刻地理解各种变量、数组和结构等是如何占用内存的,以及数组越界的过程。

⑤在调用堆栈窗口(CallStack)中观察函数调用的嵌套情况。

此窗口在函数调用关系比较复杂或递归调用的情况下,对分析故障很有帮助。

(2)单步执行程序

让程序被一步一步(行)地执行,观察分析执行过程是否符合预要求。

例如,以下程序预期的功能是从键盘上读入两个数(x和y),判断x和y是否相等,相等则在屏幕上显示x=y,不相等则显示x<>y。

这是要求实现的功能,但程序实际的运行状况却是:

无论输入什么,都会在屏幕上显示x=y和x<>y,程序肯定有问题,但表面上看却可能找不到问题所在,使用单步执行,则能定位故障点,缩小看的范围。

例如,在单步执行的过程中,若输入“2,3”,发现x和y的值的确变成了2和3,此时按道理不应执行“printf("x=y\n");”,但单步跟踪却发现被执行了,因此多半问题出在“if(x=y)”。

#include

voidmain()

{

int x,y;

printf("Pleaseinputx,y:

");

scanf("%d,%d",&x,&y);

if(x=y)

{

 printf("x=y\n");

}

else;

{

 printf("x<>y\n");

}

}

在单步执行的过程中,应灵活应用StepOver、StepInto、StepOut、RuntoCursor等方法,提高调试效率。

建议在程序调试过程中,记住并使用“StepOver、StepInto、StepOut、RuntoCursor”等菜单项的快捷键,开始时可能较生疏、操作较慢,但坚持一段时间就能生巧、效率提高。

(3)使用断言

断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。

断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。

实际应用时,可根据具体情况灵活地设计断言。

使用断言时,必须在程序的开头加上:

#include

①可用断言来确认函数的参数。

示例:

假设某函数参数中有一个指针,那么使用指针前可对它检查,以防止其他人调用本函数时使用空指针作参数。

代码如下:

intexam_fun(unsignedchar*str)

{

   assert(str!

=NULL); //断言“指针不为空”,若“空”(断言不成立)则报错

   ...//otherprogramcode

}

②可用断言来确认是否发生了不该发生的情况。

示例:

以下程序段运行结果有错,检查起来很困难而且搞了很久都不知是什么地方有问题。

因此,建议分析程序的正常运行情况应该是什么,运行过程中是否出了异常,针对所有(或关键状态)应当正常的情况,使用断言,就很有可能发现异常原因,且调试效率很高。

针对该程序段,我们断言(断定)变量i的取值应该为“i>=0&&i

for(i=0;i

{

   ...//otherprogramcode

assert(i>=0&&i

array[i]=i;

   ...//otherprogramcode

}

断言不成立时(一出现异常),系统将立即报错,此时可进入程序调试状态,检查程序的运行情况。

(4)与调试相关的操作菜单:

Build菜单

Compile:

快捷键Ctrl+F7。

编译当前处于源代码窗口中的源程序文件,以便检查是否有语法错误或警告,如果有的话,将显示在Output输出窗口中。

Build:

快捷键F7。

对当前工程中的有关文件进行连接,若出现错误的话,也将显示在Output输出窗口中。

Execute:

快捷键Ctrl+F5。

运行(执行)已经编译、连接成功的可执行程序(文件)。

StartDebug:

选择该项将弹出子菜单,其中含有用于启动调试器运行的几个选项。

例如其中的Go选项用于从当前语句开始执行程序,直到遇到断点或遇到程序结束;StepInto选项开始单步执行程序,并在遇到函数调用时进入函数内部再从头单步执行;RuntoCursor选项使程序运行到当前鼠标光标所在行时暂停其执行(注意,使用该选项前,要先将鼠标光标设置到某一个你希望暂停的程序行处)。

执行该菜单的选择项后,就启动了调试器,此时菜单栏中将出现Debug菜单(而取代了Build菜单)。

(5)与调试相关的操作菜单:

Debug菜单

启动调试器后才出现该Debug菜单(而不再出现Build菜单)。

Go:

快捷键F5。

从当前语句启动继续运行程序,直到遇到断点或遇到程序结束而停止(与Build→StartDebug→Go选项的功能相同)。

Restart:

快捷键Ctrl+Shift+F5。

重新从头开始对程序进行调试执行(当对程序做过某些修改后往往需要这样做!

)。

选择该项后,系统将重新装载程序到内存,并放弃所有变量的当前值(而重新开始)。

StopDebugging:

快捷键Shift+F5。

中断当前的调试过程并返回正常的编辑状态(注意,系统将自动关闭调试器,并重新使用Build菜单来取代Debug菜单)。

StepInto:

快捷键F11。

单步执行程序,并在遇到函数调用语句时,进入那一函数内部,并从头单步执行(与Build→StartDebug→StepInto选项的功能相同)。

StepOver:

快捷键F10。

单步执行程序,但当执行到函数调用语句时,不进入那一函数内部,而是一步直接执行完该函数后,接着再执行函数调用语句后面的语句。

StepOut:

快捷键Shift+F11。

与“StepInto”配合使用,当执行进入到函数内部,单步执行若干步之后,若发现不再需要进行单步调试的话,通过该选项可以从函数内部返回(到函数调用语句的下一语句处停止)。

RuntoCursor:

快捷键Ctrl+F10。

使程序运行到当前鼠标光标所在行时暂停其执行(注意,使用该选项前,要先将鼠标光标设置到某一个你希望暂停的程序行处)。

事实上,相当于设置了一个临时断点,与Build→StartDebug→RuntoCursor选项的功能相同。

Insert/RemoveBreakpoint:

快捷键F9。

本菜单项并未出现在Debug菜单上(在工具栏和程序文档的上下文关联菜单上),列在此处是为了方便大家掌握程序调试的手段,其功能是设置或取消固定断点——程序行前有一个圆形的黑点标志,表示已经该行设置了固定断点。

另外,与固定断点相关的还有Alt+F9(管理程序中的所有断点)、Ctrl+F9(禁用/使能当前断点)。

VC6.0常见编译错误提示

(1)errorC2001:

newlineinconstant

编号:

C2001

直译:

在常量中出现了换行。

错误分析:

1.①字符串常量、字符常量中是否有换行。

2.②在这句语句中,某个字符串常量的尾部是否漏掉了双引号。

3.③在这语句中,某个字符创常量中是否出现了双引号字符“"”,但是没有使用转义符“\"”。

4.④在这句语句中,某个字符常量的尾部是否漏掉了单引号。

5.⑤是否在某句语句的尾部,或语句的中间误输入了一个单引号或双引号。

(2)errorC2015:

toomanycharactersinconstant

编号:

C2015

直译:

字符常量中的字符太多了。

错误分析:

单引号表示字符型常量。

一般的,单引号中必须有且只能有一个字符(使用转义符时,转义符所表示的字符当作一个字符看待),如果单引号中的字符数多于4个,就会引发这个错误。

另外,如果语句中某个字符常量缺少右边的单引号,也会引发这个错误,例如:

if(x=='x||x=='y'){…}

值得注意的是,如果单引号中的字符数是2-4个,编译不报错,输出结果是这几个字母的ASC码作为一个整数(int,4B)整体看待的数字。

(3)errorC2137:

emptycharacterconstant

编号:

C2137

直译:

空的字符定义。

错误分析:

原因是连用了两个单引号,而中间没有任何字符,这是不允许的。

(4)errorC2018:

unknowncharacter'0x##'

编号:

C2018

直译:

未知字符‘0x##’。

错误分析:

0x##是字符ASC码的16进制表示法。

这里说的未知字符,通常是指全角符号、字母、数字,或者直接输入了汉字。

如果全角字符和汉字用双引号包含起来,则成为字符串常量的一部分,是不会引发这个错误的。

(5)errorC2041:

illegaldigit'#'forbase'8'

编号:

C2141

直译:

在八进制中出现了非法的数字‘#’(这个数字#通常是8或者9)。

错误分析:

如果某个数字常量以“0”开头(单纯的数字0除外),那么编译器会认为这是一个8进制数字。

例如:

“089”、“078”、“093”都是非法的,而“071”是合法的,等同于是进制中的“57”。

(6)errorC2065:

'xxxx':

undeclaredidentifier

编号:

C2065

直译:

标识符“xxxx”未定义。

错误分析:

首先,解释一下什么是标识符。

标志符是程序中出现的除关键字之外的词,通常由字母、数字和下划线组成,不能以数字开头,不能与关键字重复,并且区分大小写。

变量名、函数名、类名、常量名等等,都是标志符。

所有的标志符都必须先定义,后使用。

标志符有很多种用途,所以错误也有很多种原因。

1.如果“xxxx”是一个变量名,那么通常是程序员忘记了定义这个变量,或者拼写错误、大小写错误所引起的,所以,首先检查变量名是否正确。

(关联:

变量,变量定义)

2.如果“xxxx”是一个函数名,那就怀疑函数名是否没有定义。

可能是拼写错误或大小写错误,当然,也有可能是你所调用的函数根本不存在。

还有一种可能,你写的函数在你调用所在的函数之后,而你有没有在调用之前对函数原形进行申明。

(关联:

函数申明与定义,函数原型)

3.如果“xxxx”是一个库函数的函数名,比如“sqrt”、“fabs”,那么看看你在cpp文件已开始是否包含了这些库函数所在的头文件(.h文件)。

例如,使用“sqrt”函数需要头文件math.h。

如果“xxxx”就是“cin”或“cout”,那么一般是没有包含“iostream.h”。

(关联:

#include,cin,cout)

4.如果“xxxx”是一个类名,那么表示这个类没有定义,可能性依然是:

根本没有定义这个类,或者拼写错误,或者大小写错误,或者缺少头文件,或者类的使用在申明之前。

(关联:

类,类定义)

5.标志符遵循先申明后使用原则。

所以,无论是变量、函数名、类名,都必须先定义,后使用。

如使用在前,申明在后,就会引发这个错误。

6.C++的作用域也会成为引发这个错误的陷阱。

在花括号之内变量,是不能在这个花括号之外使用的。

类、函数、if、do(while)、for所引起的花括号都遵循这个规则。

(关联:

作用域)

7.前面某句语句的错误也可能导致编译器误认为这一句有错。

如果你前面的变量定义语句有错误,编译器在后面的编译中会认为该变量从来没有定义过,以致后面所有使用这个变量的语句都报这个错误。

如果函数申明语句有错误,那么将会引发同样的问题。

(7)errorC2086:

'xxxx':

redefinition

编号:

C2374

直译:

“xxxx”重复申明。

错误分析:

变量“xxxx”在同一作用域中定义了多次。

检查“xxxx”的每一次定义,只保留一个,或者更改变量名。

(8)errorC2374:

'xxxx':

redefinition;multipleinitialization

编号:

C2374

直译:

“xxxx”重复申明,多次初始化。

错误分析:

变量“xxxx”在同一作用域中定义了多次,并且进行了多次初始化。

检查“xxxx”的每一次定义,只保留一个,或者更改变量名。

(9)C2143:

syntaxerror:

missing';'before(identifier)'xxxx'

编号:

C2143

直译:

在(标志符)“xxxx”前缺少分号。

错误分析:

这是VC6的编译期最常见的误报,当出现这个错误时,往往所指的语句并没有错误,而是它的上一句语句发生了错误。

其实,更合适的做法是编译器报告在上一句语句的尾部缺少分号。

上一句语句的很多种错误都会导致编译器报出这个错误:

1.上一句语句的末尾真的缺少分号。

那么补上就可以了。

2.上一句语句不完整,或者有明显的语法错误,或者根本不能算上一句语句(有时候是无意中按到键盘所致)。

3.如果发现发生错误的语句是cpp文件的第一行语句,在本文件中检查没有错误,但其使用双引号包含了某个头文件,那么检查这个头文件,在这个头文件的尾部可能有错误。

(10)errorC4716:

'xxx':

mustreturnavalue

编号:

C4716

直译:

“xxx”必须返回一个值。

错误分析:

函数声明了有返回值(不为void),但函数实现中忘记了return返回值。

要么函数确实没有返回值,则修改其返回值类型为void,要么在函数结束前返回合适的值。

(11)warningC4508:

'main':

functionshouldreturnavalue;'void'returntypeassumed

编号:

C4508

直译:

main函数应该返回一个值;void返回值类型被假定。

错误分析:

1.函数应该有返回值,声明函数时应指明返回值的类型,确实无返回值的,应将函数返回值声明为void。

若未声明函数返回值的类型,则系统默认为整型int。

此处的错误估计是在main函数中没有return返回值语句,而main函数要么没有声明其返回值的类型,要么声明了。

2.warning类型的错误为警告性质的错误,其意思是并不一定有错,程序仍可以被成功编译、链接,但可能有问题、有风险。

(12)warningC4700:

localvariable'xxx'usedwithouthavingbeeninitialized

编号:

C4700

直译:

警告局部变量“xxx”在使用前没有被初始化。

错误分析:

这是初学者常见的错误,例如以下程序段就会造成这样的警告,而且程序的确有问题,应加以修改,尽管编译、链接可以成功——若不修改,x的值到底是多少无法确定,是随机的,判断其是否与3相同没有意义,在运气不好的情况下,可能在调试程序的机器上运行时,结果看起来是对的,但更换计算机后再运行,结果就不对,初学者往往感到迷惑。

intx;

if(x==3)printf("hello");

VC60.0常见链接错误

(1)errorLNK2001:

unresolvedexternalsymbol_main

编号:

LNK2001

直译:

未解决的外部符号:

_main。

错误分析:

缺少main函数。

看看main的拼写或大小写是否正确。

(2)errorLNK2005:

_mainalreadydefinedinxxxx.obj

编号:

LNK2005

直译:

_main已经存在于xxxx.obj中了。

错误分析:

直接的原因是该程序中有多个(不止一个)main函数。

这是初学C++的低年级同学在初次编程时经常犯的错误。

这个错误通常不是你在同一个文件中包含有两个main函数,而是在一个project(项目)中包含了多个cpp文件,而每个cpp文件中都有一个main函数。

引发这个错误的过程一般是这样的:

你写完成了一个C++程序的调试,接着你准备写第二个C++文件,于是你可能通过右上角的关闭按钮关闭了当前的cpp文件字窗口(或者没有关闭,这一操作不影响最后的结果),然后通过菜单或工具栏创建了一个新的cpp文件,在这个新窗口中,程序编写完成,编译,然后就发生了以上的错误。

原因是这样的:

你在创建第二个cpp文件时,没有关闭原来的项目,所以你无意中新的cpp文件加入你上一个程序所在的项目。

切换到“FileView”视图,展开“SourceFiles”节点,你就会发现有两个文件。

在编写C++程序时,一定要理解什么是Workspace、什么是Project。

每一个程序都是一个Project(项目),一个Project可以编译为一个应用程序(*.exe),或者一个动态链接库(*.dll)。

通常,每个Project下面可以包含多个.cpp文件,.h文件,以及其他资源文件。

在这些文件中,只能有一个main函数。

初学者在写简单程序时,一个Project中往往只会有一个cpp文件。

Workspace(工作区)是Project的集合。

在调试复杂的程序时,一个Workspace可能包含多个Project,但对于初学者的简单的程序,一个Workspace往往只包含一个Project。

当完成一个程序以后,写另一个程序之前,一定要在“File”菜单中选择“CloseWorkspace”项,已完全关闭前一个项目,才能进行下一个项目。

避免这个错误的另一个方法是每次写完一个C++程序,都把VC6彻底关掉,然后重写打开VC6,写下一个程序。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 电力水利

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1