赞
踩
在 C++ 程序设计中,有以下 3 类错误:
我们结合具体案例,来讨论这三种错误。
语法错误又分为两类: 编译错误 (Compile error),连接错误 (link error).
前者是编译过程中发生的错误,导致无法生成目标文件,
可能的原因有:分号遗漏或多余,括号不匹配,标识符未定义,访问类的 private 成员等。
后者是生成目标文件后,连接过程中发生错误,是针对有多个源文件的情况,
可能的原因有:全局变量重复定义,函数名冲突等。
根据报错的信息,我们修改代码,再次编译和连接,直到没有语法错误为止。
例如,我们有如下代码段:
- #include <stdio.h>
-
- class Cat {
- // ...
- }
-
- int main() {
- cat* pCat = new cat;
- delete pCat;
- return scanf("%*s");
- }
编译会提示,未定义标识符 cat,因为,C++ 严格区分大小写,cat 应该写成 Cat。
另一个案例:
- #include <stdio.h>
-
- int main() {
- int a = 3;
- double b = 2:
- printf("%d\n", a % b);
- return scanf("%*s");
- }
取模运算,只适用于整型数据,而我们对浮点数 b 取摸,因此会报错。
有时候,我们遇到的只是 Warning ,可以生成目标文件,但为了保险,我们还是应该修改代码,尽量达到 0 error, 0 warning。
运行过程中,程序可能出现错误。这些错误属于运行错误,不会在编译过程中显现。
例如,我们有以下代码:
- #include <stdio.h>
-
- int main() {
- int a = 1;
- int b = 1;
- scanf("%d %d", &a, &b);
- printf("%d\n", a / b);
- return scanf("%*s");
- }
如果在运行过程中,输入的第 2 个数为0,则会引发除数为0的异常。
我们可以在做除法前,判断除数是否为 0,从而避免这种异常。
或者使用 try-catch 语句,来处理可能发生的异常。
- #include <stdio.h>
-
- int main() {
- int a = 1;
- int b = 1;
- scanf("%d %d", &a, &b);
- try {
- printf("%d\n", a / b);
- }
- catch (...) {
- printf("Runtime error!\n");
- scanf("%*s");
- return 1;
- }
- return scanf("%*s");
- }
引发运行错误的原因,不仅有除数为0,还有下标越界,栈溢出等。
下面这段代码, 大家不要轻易运行!
- #include <stdio.h>
-
- int main() {
- int* p = NULL;
- while (true) {
- p = new int[1024];
- }
- return scanf("%*s");
- }
它可以通过编译,但运行时会无限开辟内存,导致内存耗尽,发生严重错误。
我在 x64 Win10 系统中运行,约 80s 后出现黑屏,系统无响应的情况。
如果增加 try-catch 机制,则可以及时结束这种局面:
- #include <stdio.h>
-
- int main(int argc, char** argv) {
- int* p = 0;
- try {
- while (true) {
- p = new int[1024];
- }
- }
- catch (...) {
- printf("Runtime error!\n");
- scanf("%*s");
- return 1;
- }
- return scanf("%*s");
- }
这种情况,编译没有报错,运行也没有抛出异常,但是输出的结果不正确。
我们看这个案例:
- #include <stdio.h>
- #define LEN 8
-
- int main() {
- int i = 0;
- int* ar = new int[LEN];
- for (i = 0; i > LEN; ++i) {
- if (1 > scanf("%d", ar + i)) {
- scanf("%*s");
- }
- }
- while (--i >= 0) {
- printf("%d,", ar[i]);
- }
- delete[] ar;
- return scanf("%*s");
- }
第 7 行的 i > LEN 应改为 i < LEN,否则程序不会进入循环,不输出任何内容。
我们再看另一个案例:
- #include <stdio.h>
-
- class Date {
- private:
- int year;
- int month;
- int day;
- public:
- Date(int y, int m, int d) {
- year = y;
- m = month;
- day = d;
- }
- // Other functions...
- }
-
- int main() {
- Date* date = new Date(2001, 6, 10);
- // ...
- delete date;
- return scanf("%*s");
- }
在 Date 的构造函数里,m = month 应改为 month = m;
否则将导致 month 成员没有被初始化,运行结果错误。
如果用严格检查的编译软件,例如 VS 2019,它会在编译时给出 warning,说 month未初始化,从而提示我们,这里可能有错误。
不过,有些语义错误,并不引发 warning ,只在运行过程中表现,很难被察觉。
想要避免这些语义错误,我们就必须认真分析代码。
之后我会总结更多案例。^_^
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。