C++程序常见的错误_1

今天,我总结了一些 C++ 程序常见的错误,大家尽量避免.

1. 运算顺序错误

我们来看一个,判断奇偶数的程序:

#include <stdio.h>

int main(int argc, char** argv) {
	int n = 0;
	printf("Input a natural number:\n");
	while (1 == scanf("%d", &n)) {
		if (1 & n == 0) { // Notice here!
			printf("Even\n");
		} else {
			printf("Odd\n");
		}
	}
	return scanf("%*s%*s");
}

编译没有错,但运行得到错误的结果;

只有输入 0 的时候,才会输出 "Even",其他情况都输出 "Odd".

我们注意到,== 属于关系运算,而 & 是位运算,前者优先于后者.

"1 & n == 0" 其实相当于 "1 & (n == 0)",它等价于 "n == 0";

所以,我们应该将 if 中的条件,改为 "(1 & n) == 0",或者 "!(1 & n)".

一些严格检查的 IDE,会在这样的地方给出 warning,建议我们加圆括号.


2. 下标越界错误

在使用数组时,我们可能使用了错误的下标,导致非法访问内存.

下面这个案例,编译不会报错,运行也没有抛出异常,但存在问题:

#include <stdio.h>
#include <time.h>
#include <random>
#define LEN	10

int main(int argc, char** argv) {
	srand(time(NULL));
	int* p = new int[LEN];
	int i = 0;
	for (; i < LEN; ++i) {
		p[i] = rand() % 50;
	}
	while (1 == scanf("%d", &i)) {
		// There should be something extra;
		printf("p[%d]= %d\n", i, p[i]);
	}
	delete[] p;
	return scanf("%*s");
}

这段程序的本意,是生成 10 个一定范围内的随机数,放在数组中,并通过下标来访问;

但是,输入下标后,并没有检查其合法性,这样可能会越界;

所以,正确的写法,应该在 printf 之前,判断 i 是不是合法的下标.

还有另一种方法,

我们用向量 (vector) 代替数组,用 at 函数来代替下标,向量定义在 <vector> 中,我们需要添加相应的指令;

当下标越界时,我们可以捕获一个叫 out_of_range 的异常,它定义在 <stdexcpt> 头文件中,我们同样需要添加包含指令;

注意,在这些 #include 指令后面,需要加上 "using namespace std;",否则会编译出错.

#include <stdio.h>
#include <random>
#include <stdexcept>
#include <vector>
using namespace std;
#define LEN	10

int main(int argc, char** argv) {
	srand(time(NULL));
	vector<int> vec;
	int i = 0;
	for (; i < LEN; ++i) {
		vec.push_back(rand() % 50);
	}
	while (1 == scanf("%d", &i)) {
		try {
			printf("p[%d]= %d\n", i, vec.at(i));
		}
		catch(out_of_range e) {
			printf("Wrong index!\n");
		}
	}
	return scanf("%*s");
}

3. 未初始化的错误

有时候,我们没有给变量初始化,导致运行出奇特的结果.

#include <stdio.h>
#define LEN	10

int main(int argc, char** argv) {
	int* p = new int[LEN];
	int i = LEN;
	for (; --i; p[i] = i); // Wrong code;
	for (i = 0; i < LEN; ++i) {
		printf("%d, ", p[i]);
	}
	delete[] p;
	return scanf("%*s");
}

我们看第一个 for 循环,它的条件是 --i,相当于 --i != 0;

当 i = 0 时,循环结束; 我们只对 p[1] 到 p[9] 进行了初始化,而没有对 p[0] 初始化;

因此,这段程序输出的结果,第一个数一般都很奇怪.

正确的做法是,把 --i 改为 --i >= 0.

当然,我们也可以,把 int* p = new int[LEN] 改为 int p[LEN] = {};

然后去掉后面的 delete[] p;

定义数组时,如果使用 new 运算,属于动态分配内存,使用完毕后,需要用 delete[] 来释放内存;这种情况下,数组元素的值,可能没有被初始化.

而如果使用大括号,对数组进行初始化,属于静态分配内存,不用再写 delete.

当大括号内容为空时,数值型的元素都初始化为 0,其他类型的元素为默认初始值.

当然,我们可以在大括号里,直接初始化,例如:

int ar[6] = {
	1, 5, 2
};

对于前 3 个元素,我们直接定义了初始值,对于后面的元素,会被设为 0.

以上写法相当于:

int ar[6] = {
	1, 5, 2, 0, 0, 0
};

4. 类型转换错误

我们看一个例子:

#include <iostream>
#include <string>
using namespace std;

int main(int argc, char** argv) {
	string str = "AB";
	int num = 6;
	str = (num + '0') + str;
    // Error here!
	printf("%s\n", str.c_str());
	num = 3;
	str += (num + '0');
	cout << str << endl;
	return scanf("%*s");
}

在 str = (num + '0') + str 这里,编译出错,这涉及到类型转换的问题;

因为 num 是整型, '0' 是字符型,两者同时存在于一个表达式,则将后者也转为整型,

故 (num + '0') 结果是 int 型,它与 string 型之间不支持 + 运算符,于是报错.

正确的写法,是显式转换类型:

str = (char)(num + '0') + str;

后面的 str += (num + '0') ,不需要写 (char),编译和运行都正常,

这是因为,string 的 += 运算符,会把后面的 int 理解为字符的 ASCII 码.

类型转换分为两种,显式转换,隐式转换.

显式转换,就是在需要转换的变量前,加一对圆括号,括号内写出类型,例如:

int n = 10;
float a = (float)n;

我们对 a 赋值的时候,使用了 int 型变量 n 的值,此时需要将其转换为 float 型.

隐式转换,则是什么都不加,让编译器去自动转换,例如:

int n = 10;
float a = n;

这种情况下,编译器会自动将第 2 行的 n 转换为 float 型.

不过,我建议大家用显式转换,不推荐隐式转换,因为后者容易出错.


今天,我就介绍到这里,以后继续更新. ^_^

猜你喜欢

转载自blog.csdn.net/Cat_2019_Cpp/article/details/121573756