C++ 常见错误

C++ 常见错误,第1张

C++ 常见错误

今天, 我们继续来排错.

从键盘输入整数, 这是很常见的输入方式, 也是容易出错的地方.

我们看一下案例:

#include 
#define NUM	6

int main(int argc, char** argv) {
	int n = 0;
	int i = 0;
	double sum = 0;
	printf("Input %d integers:n", NUM);
	for (; i < NUM; ++i) {
		scanf("%d", &n);
		sum += n;
	}
	printf("Ave= %.3Lfn", sum / NUM);
	return scanf("%*s");
}

这段程序的功能, 是输入 6 个整数, 计算其平均数, 我们正常输入, 不会出错;

但是, 如果输入错误的内容 (例如 "A", "3.14"), 它们不能作为整数, 则会得到错误的结果;

并且, 中间一个错误内容, 会影响后续所有输入.

这就需要我们, 利用 scanf 函数的返回值, 来判断输入的合法性.

scanf 返回一个 int 值, 它表示成功读入变量的参数个数;

例如, scanf("%d", &n) 在读取 "23" 时, 成功读取, 返回 1; 而读取 "A" 时, 则返回 0;

当读取失败时, 错误的内容, 会停留在键盘缓冲区;

我们必须清除它们, 否则它会被下一次 scanf 读取到, 从而继续导致错误.

这就好比, 水管中间被异物堵住了, 必须清除异物, 才能让水流恢复正常.

我们使用 scanf("%*s") 可以扫描一个字符串, 但不存入变量中, 遇到 Enter, 空格或 Tab 为止;

这种方法, 可以去除最近一次错误的输入, 从而保证后续输入正常进行:

if (1 > scanf("%d", &n)) {
		scanf("%*s");
}

注意, scanf("%*s") 永远返回 0, 因为它没有读入任何内容;

我习惯将 main 函数结尾的 "return 0;" 写成 "return scanf("%*s");",

这样可以, 在程序运行结束前, 等待我们输入, 从而让运行结果停留在屏幕中;

否则, 我们可能会遇到, 程序运行的界面一闪而过, 没来得及看结果的情况.

如果我们想用 cin, 就不能这么简单了.

cin, cout, 属于运算符, 而 scanf 和 printf 属于函数, 它们的原理并不相同;

cin 也有返回值, 当它成功读取时, 返回非 0 值, 否则返回 0;

下方的代码, 可以连续读取任意个整数, 同时进行求和, 直到输入不能解读为整数的内容为止:

while (cin >> n) {
	sum += n;
}

注意, 我们最好不要混合使用 scanf 和 cin, 大家试一下就知道了, 容易出现错误.

相对于 scanf 而言, cin *** 作起来不是很方便;

如果你非要用 cin, 最好是自定义一个函数, 来读取字符串中的整数:

int ScanInt(char* str, int& num) {
	num = 0;
	if (nullptr == str) {
		return -1;
	}
	bool neg = false;
	int i = 0;
	char ch = str[0];
	if ('-' == ch) {
		neg = true;
		++i;
	}
	for (; '' != (ch = str[i]); ++i) {
		if (ch < '0' || ch > '9') {
			num = 0;
			return -1;
		}
		num = ch - '0' + 10 * num;
	}
	if (neg) {
		num = -num;
	}
	return 0;
}

第 1 个参数 str, 是我们输入的内容;

第 2 个参数 num, 是存放结果的变量, 注意, 它是引用的形式.

返回值为 0, 表示成功读取, 返回 -1 则表示输入错误.

我们以字符串的形式读取数据, 这样的好处是, 遇到错误输入后, 不需要清空缓冲区.

主函数如下:

#include 
using namespace std;
#include "Smile.h"
#define NUM	6

int main(int argc, char** argv) {
	char str[32] = "";
	int n = 0;
	int i = 0;
	Scanner* scn = new Scanner;
	for (; i < NUM; ++i) {
		cin >> str;
		if (scn->ScanInt(str, n)) {
			cout << "Wrong input!n";
		}
		else {
			cout << n << endl;
		}
	}
	delete scn;
	cin >> str;
	return 0;
}

运行结果如下:

 类似地, 我们可以再定义一个 ScanDouble, 读取字符串中的浮点数,

然后封装成一个类:

class Scanner {
public:
	// Read a double float number from str;
	int ScanDouble(char* str, double& res) {
		res = 0;
		if (nullptr == str) {
			return -1;
		}
		bool neg = false;
		int i = 0;
		char ch = str[0];
		if ('-' == ch) {
			neg = true;
			++i;
		}
		for (; '' != (ch = str[i]); ++i) {
			if ('.' == ch) {
				break;
			}
			if (ch < '0' || ch > '9') {
				res = 0;
				return -1;
			}
			res = (double)(ch - '0') + 10 * res;
		}
		if ('' == ch) {
			return 0;
		}
		double pos = 1;
		while ('' != (ch = str[++i])) {
			if (ch < '0' || ch > '9') {
				res = 0;
				return -1;
			}
			pos *= 0.1;
			res += (double)(ch - '0') * pos;
		}
		if (neg) {
			res = -res;
		}
		return 0;
	}

	// Read an int number from str;
	int ScanInt(char* str, int& num) {
		num = 0;
		if (nullptr == str) {
			return -1;
		}
		bool neg = false;
		int i = 0;
		char ch = str[0];
		if ('-' == ch) {
			neg = true;
			++i;
		}
		for (; '' != (ch = str[i]); ++i) {
			if (ch < '0' || ch > '9') {
				num = 0;
				return -1;
			}
			num = ch - '0' + 10 * num;
		}
		if (neg) {
			num = -num;
		}
		return 0;
	}
};

不过, 我还是建议, 大家使用 scanf 和 printf, 尽量不要用 cin 和 cout.

这 2 个函数可以优化, 比如说, 可以一次性读入多个数据.

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5635618.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存