C++杂谈,关于“字节”的流

沃尔特·萨维奇的书 《Problem Solving with C++》 第六章是关于“流”与“输入输出”的。这两个概念都比较抽象,所以这篇文章的主体将会是相关内容的杂谈,而结尾给出一个关系并不密切的实例,实例主要是为了后一篇笔记实现一个项目的“测试驱动程序”来铺路。

IO,即 in and out, 输入和输出。如果有数据从键盘、文件、服务器流向一个进程,则称为“输入”;如果有数据从一个进程流向屏幕、服务器或文件,则称为“输出”;基于硬件来说,进与出是以“计算处理器”为参照物的。

那么“数据”和“流”(名词)究竟是什么关系呢?

程序在编译、链接后仍需要装入内存(RAM)中,才能运行。如果操作系统使用的是“连续分配存储”的管理策略,则某一段进程与它使用的数据占用的随机内存单元的逻辑地址是相邻的。物理地说,一个进程是 RAM 里一段存储单元里“有意义”的内容,一个存储单元存储了8比特的信息,被称为1字节的“进程”;而进程需要处理信息,这些信息同样会被一字节一字节地码在 RAM 里,这一段单元里要被处理的信息叫“数据”(所以也有1字节的数据的说法)数据是存储器中一段连续的字节里保存的有意义的二进制位。

而“流”是一个数据结构,或者,是一个“对象”, 是一个计算机系统用来实现文件读写、设备控制的工具。 可以想象,第一个写出了“流”对象的人一定幻视着一个字节一个字节的数据像一个个水分子一样、顺序不绝地流向存储它们的地方。所以他才设计了“流”,一种按顺序运输数据的对象。

在 C++ 中流对象包含在库文件<iostream>中,甚至重载了运算符 << 和 >> 来模拟数据“流动”的形态,这两个移位运算符各会返回一个“流引用”,而流的引用对象可以被自动转换为布尔值,如果流能顺利提取数据,就返回 true; 反之则返回 false.

善用流对象的“位移”运算符和按字符读写的低级机制,可以实现更安全地读取键盘输入。这里所谓的“低级机制”,主要指程序在读入键盘输入的字符时,不会自动地把它们转化为日常所理解的数据类型。从而对输入行为可以进行更加严格的控制。代码如下:

//  演示用 newLine() 和 getInput() 来核准输入

void newLine();
//  pre: 无 (读入键盘输入流内这一行的所有信息)
//  post: 丢弃这一行中读入的输入,在另起一行

bool getInt(int& number);
//  pre: 无 (引导用户输入值,并确认)
//  post: 为number赋值, 若赋值失败,则返回 false

int main()
{
    int n = 999;
    
    cout << "Demo 演示如何合法地读入一个整数、并忽视错误的输入。\n";
    getInt(n);
    cout << "最终写入的值是 " << n << endl
         << "Demo 演示结束。\n";
    return 0;
}

void newLine()
{
    char symbol;
    do
    {
        cin.get(symbol);
    }   while (symbol != '\n');
    cout << endl;
}

bool getInt(int& number)
{
    char ans;
    do
    {
        cout << "请输入一个数字: ";
        if( cin >> number )
        {
            cout << "输入的数字是 " << number <<" 对吧? (yes/no): ";
            cin >> ans;
            newLine();
        }
        else
        {
            cout << " Input Error!\n"; 
            number = 0;
            return false;
        }
    } while ((ans != 'Y') && (ans != 'y'));  
    return true;
}

运行结果:

核准输入.cpp 结果-1

核准输入.cpp 结果-2

大致如此。


塞维奇教授的书里把“函数的默认实参”也安排在 “字符输入输出” 这一节,我发现函数在给形式参数留下默认实参值时,也必须满足“按顺序”且“前后相连”,如同一个参数流。所以再附上相关的一点随笔:

如果一个函数 defaultArgs() 接受 4 个参数 arg1, arg2, arg3, arg4;而还可以重载为一个不接受参数的简化版函数,而简化版的 defalutArgus() 内部逻辑一样,只是4个参数在函数体内用了各自的默认值。这种时候,可以直接在函数的参数列表内标明默认实参,将4个参数分别写明调用时的初始值,就可以在调用时不传任何实参;也可以传入4个类型相符的新实参。

指定默认参数的规则是:可以指定 0-4 个,但一旦指定后,也就相应的确定了这个函数的简化形式需要几个实参,比如,指定了两个默认实参,则它的简化调用形式只能接受两个实参,依次填充进 arg1 和 arg2; 而默认实参也只能指定4个参数中的最后几(0-4)个,前面的形参空缺,等待按简化方式调用时传入读入。

读入的实参流必须依次严格与空缺的形参相符。

posted @ 2025-08-17 23:41  吴萝卜  阅读(7)  评论(0)    收藏  举报