C++程序常见错误总结

在 C++ 程序设计中,有以下 3 类错误:

  1. 语法错误;
  2. 运行错误;
  3. 语义错误;

我们结合具体案例,来讨论这三种错误。

1. 语法错误

语法错误又分为两类: 编译错误 (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。

2. 运行错误 (Runtime error)

运行过程中,程序可能出现错误。这些错误属于运行错误,不会在编译过程中显现。

例如,我们有以下代码:

#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");
}

3. 语义错误

这种情况,编译没有报错,运行也没有抛出异常,但是输出的结果不正确。

我们看这个案例:

#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 ,只在运行过程中表现,很难被察觉。

想要避免这些语义错误,我们就必须认真分析代码。

之后我会总结更多案例。^_^

Guess you like

Origin blog.csdn.net/Cat_2019_Cpp/article/details/121529285