CC++习题与知识

[TOC]

【1】输入错误处理

cin >> int 型变量(C++)

三种情况:

  1. 输入非法:非数字、超上下限
  2. 输入合法,但不在用户要求范围内
  3. 输入合法且在用户要求范围内 √
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while (1) {
cout << "请输入x的值[0-100] : ";
cin >> x;
if (!cin.good()) { //或!cin. /cin.fail()
cin.clear(); //清除内部错误状态标记位
cin.ignore(65536, '\n');//清除缓冲区中字符,到\n为止,最多清65536个,可改
continue; //也可#include <limits>后INT_MAX,或直接2147483647,
}
if (x >= 0 && x <= 100)
break;
}
/*cin.ignore(cin.rdbuf()->in_avail(),'\n');
返回缓冲区中现有字符数
VS :可用
Dev :无论缓冲区中现有多少字符,均返回0,不可用
cin.ignore(numeric_limits<streamsize>::max(),'\n');
返回流缓冲区的最大容量
VS :直接可用(64位整数)
当 #include <Window.h> 时不可用
Dev :加 #include <limits>(32位整数)

实际上也可不要continue,改用else if

1
2
3
4
5
6
7
8
9
10
while (1) {
cout << "请输入x的值[0-100] : ";
cin >> x;
if (!cin.good()) {
cin.clear();
cin.ignore(65536, '\n');
}
else if (x >= 0 && x <= 100)
break;
}

模板:

while (1) {
提示并输入x
if (输入错误) {
清除错误标记
清空缓冲区
}
else if (x >= 0 && x <= 100)
break;
}

scanf(“%d”, &int变量) (C方式)

scanf返回值:正确按指定格式输入变量的个数,也即能正确接收到值的变量个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while (1) {
printf("请输入x的值[0-100] : ");
ret = ;
if (scanf("%d", &x) != 1){
while(getchar()!='\n')
;
}
else if (x >= 0 && x <= 100) {
break;
}
printf("输入有错[ret=%d x=%d],请重新输入\n", ret, x); //这句不一定要,看情况
}
/*scanf("%*[^\n]"); 清缓冲区
setbuf(stdin,NULL); 清缓冲区时,setbuf可用,但是只能用一次
char ch;
while(((ch=getchar())!=EOF) && ch != '\n')
; 也是正确的,EOF = end of file,暂时不了解,可避免用.dat文件重定向输入会出问题(否则死循环非法)

==cin 与 scanf 在输入超上下限时有差异==

**cin :**状态 false,值上限/随机

**scanf:**无状态观察,返回正确的1,截断赋值

由此引申出错误输入处理的通用规则

  1. 上述处理规则适用于各种int

    不含char/unsigned char,因为输入方式不同。(只是说,运算时可以当作一字节整数处理)

  2. 如何判断char/unsigned char 型的输入错误?

  3. 如何判断float/double 型的输入错误?

  4. 如何判断bool 型的输入错误?

计算机的思维

  1. 读入1.1时,读完"1"正确,".1"后续留在缓冲区
  2. 读入126abc,同理

所以一般来说,我们都依次处理每个输入,

若本次输入正确,后续则留在缓冲区

否则,清空全部缓冲区

其他问题

  1. 对于多个输入,有没有标准处理非法?

    没有

  2. 无特殊约定下的错误处理准则?

    一次读一个,cin若false则清空缓冲区,否则不清

  3. 在实际应用中,如何尽可能地避免输入错误?

    多选择,少输入,从用户角度避免输入错误,而不是错误后纠正

【2】打印月历

闰年表达式

1
leap = (y % 4 == 0 && y % 100 != 0 || y % 400 == 0)? 1 : 0;

控制输出

1
2
3
if(week == 6)
cout << endl; //周六换行
week = (++week) % 7; //使得week在0-6间反复

【3】光标移动

键盘键

  1. 普通字符:

    一键单码(ASCII码)

  2. 箭头键:

    一键双码

    224 + 72/80/75/77(上下左右)

    ​ H P K M

用户选项与参数用法

有时比如用户要输入的选项是0-6,那我们可以用这种选项来作为必要的参数输入给函数,控制函数的某种功能是否开启

习惯培养

  1. 多用符号常量(宏定义)全局常量(const)

    对于一些固定的参数,虽然说对于本题固定,但是尽量不要写死参数

    不允许使用全局变量,但是鼓励多用全局常量

  2. 条件运算符

    对于只有两个出口的函数,往往可以用?:来代替,简化代码,看起来也方便

  3. 多条件的int 整数选择,用switch-case

  4. 循环的选择,用1,用loop?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    while(loop){
    sel = menu();
    switch(sel){
    case '1':
    case '2':
    move(hout, 0, sel-'1');
    break;
    case '3':
    case '4':
    move(hout, 1, sel-'3');
    break;
    case '0':
    loop = 0; //巧妙
    break;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    while(loop){
    switch(_getch()){
    case 'L':
    case 'l': //两个连用,遇到break才会出switch
    if(++x > MAX_X)
    x = rool ? 1 : MAX_X;
    break;
    case 'J':
    case 'j':
    ...
    ...
    default:
    continue;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    case 224:
    if(!move_by_arrow)
    continue; //奇妙控制是否能够箭头,在要动用的前面截路,加个判断+continue
    switch(_getch()){
    case 72://'H'
    //上
    ......
    }
    break;

【4】递归分解正整数

预备知识

1
2
3
4
int a = -2147483648, b = -a;
cout << a << endl;//得到-2147483648
cout << -a << endl;//得到-2147483648
cout << b << endl;//得到-2147483648

正序

输入:12345

输出:1 2 3 4 5

1
2
3
4
5
6
void convert(int n)
{
if(n/10)
convert(n/10);
cout << (char)('0' + n%10) << " ";//记住 整数转字符 输出的方法
}

逆序

输入:12345

输出:5 4 3 2 1

1
2
3
4
5
6
void convert(int n)
{
cout << (char)('0' + n%10) << " ";//把输出放前面,会导致先输出个位数字!
if(n/10)
convert(n/10);
}