C++沒有直接定義任何用於執行輸入或輸出(IO)的語句。作為代替,IO是通過標准類庫(standard library)提供的。IO類庫提供了一個廣泛的工具集合。但是,出於某些目的(包括本文中的例子),你可能需要了解一些基本的概念和操作。
本文中的例子使用了iostream類庫,它處理格式化輸入和輸出。Iostream的基礎是兩個類型istream和ostream,它們分別表示輸入和輸出流。流(stream)是准備讀取或寫入某個IO設備或其它設備的字符串。"流"這個術語暗示隨著時間的推移,生成了字符或消除了字符。
1、標准的輸入和輸出對象
該類庫定義了四個IO對象。為了處理輸入,我們使用了istream類型的cin對象,它是標准的輸入。輸出則使用ostream的cout對象,它通常被稱為標准的輸出。這個類庫還定義了其它的兩個ostream對象,分別是cerr和clog。Cerr對象是標准的錯誤,典型情況下用於給程序的用戶生成警告或錯誤消息。Clog對象用於生成程序的執行情況信息。
通常,系統把其中的每個對象都與程序執行的窗體相關聯。因此,當我們從cin中讀取的時候,數據來自程序執行的窗體,當我們寫入cout、cerr、clog的時候,輸出也寫入相同的窗體。大多數操作系統都給了我們重定向輸入或輸出流的方法。使用重定向可以把這些流與我們選定的文件關聯起來。
2、使用IO類庫的程序
我們已經知道了如何編譯和執行簡單的程序了,盡管這個程序沒有任何功能。在整個問題中,有幾個記錄指向同一個ISBN。我們必須把這些記錄合並成總數,這意味著我們必須知道如何添加銷售的圖書數量。
為了看到如何解決這些問題,我們先看一看兩個數字如何相加。使用IO類庫,我們可以擴展已有的main程序,讓用戶輸入兩個數字,然後打印出兩個數字的和:
#include <iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
return 0;
}
這個程序先在用戶的屏幕上輸出:
Enter two numbers:
接著等待用戶輸入信息。如果用戶在另一行輸入了:
3 7
那麼程序就生成如下的輸出信息:
The sum of 3 and 7 is 10
我們的程序的第一行是預處理程序指令(preprocessor directive):
#include <iostream>
它告訴編譯器我們希望使用iostream類庫。尖括號內的名字是頭文件(header)。每個使用了類庫的程序都必須包含相關的頭文件。#include指令必須寫在同一行中--頭文件的名字和#include必須出現在同一行。一般來說,#include指令應該出現在函數的外面。典型情況下,#include指令出現在程序文件的開頭部分。
向流寫入信息
Main主體中的第一個語句執行了一個表達式(expression)。在C++中一個表達式由一個或多個操作數和(通常)一個操作符組成。這個語句中的表達式使用輸出操作符(<<操作符)來打印標准的輸出提示:
std::cout << "Enter two numbers:" << std::endl;
這個語句兩次使用了輸入操作符。輸出操作符的每個實例都有兩個操作數:左邊的操作數必須是一個ostream對象;右邊的操作數是一個要輸出的值。該操作符把右邊的操作數寫入左邊的操作數ostream。
在C++中每個表達式生成一個結果,典型的結果是把操作符應用在操作數上生成值。在使用輸出操作符的情況下,其結果值是左邊的操作數。也就是說,輸出操作的返回值是輸出流自身。
由於這個操作符返回左邊的操作數,所以它允許我們把輸出請求串在一起。輸出提示的語句相當於:
(std::cout << "Enter two numbers:") << std::endl;
由於(std::cout << "Enter two numbers:")返回左邊的操作數,所以語句std::cout等同於:
std::cout << "Enter two numbers:";
std::cout << std::endl;
其中的endl是一個特殊值,稱為操作者(manipulator),當寫入某個輸出流的時候,它的效果是把新行寫入到輸出並且刷新與設備相關的緩沖器。通過刷新緩沖器,我們可以確保用戶立即看到寫入流的輸出信息。
注意:
程序員在調試的過程中經常插入打印語句。這種語句一般也應該刷新流。如果忘記了這種操作,在程序崩潰的時候,可能會引起輸出信息停留在緩沖器中,導致對程序在那個位置崩潰的判斷錯誤。
使用標准類庫中的名稱
細心的讀者可能注意到這個程序使用了std::cout和std::endl而不是cout和endl。std::前綴表明cout和endl這兩個名稱是在std名字空間(namespace)中定義的。名字空間允許程序員避免在類庫中定義的名稱沖突。由於標准類庫中定義的名稱都定義在名字空間中,我們可以使用相同的名稱。
類庫使用名字空間的副作用是當使用某個類庫中的名稱的時候,我們必須明確地說明自己希望從std名字空間中使用某個名稱。std::cout使用了范圍操作符(scope operator,::)來說明我們使用的cout名稱定義在std名字空間中。
從流中讀取數據
輸入提示信息之後,我們下一步需要讀取用戶的輸入信息。我們先定義了兩個變量(variable)v1和v2來保存輸入信息:
int v1, v2;
我們把這些變量定義為整型(int),它是表現整數值的內建類型。這些變量沒有被初始化,意味著我們沒有給它們賦予初始值。我們第一次使用這些變量的時候就是把值讀入它們裡面,因此不賦予初始值也是正確的。
下一個語句
std::cin >> v1 >> v2;
讀取輸入信息。輸入操作符(>>操作)與輸出操作符的行為類似。它左邊的操作數是流,右邊的操作數是對象。它從流操作數中讀取並把值存儲到右邊操作數中。與輸出操作符類似,輸入操作符返回左邊的操作數。由於這種操作返回左邊的操作數,我們就可以把一組輸入請求合並成為一個語句。換句話說,這個輸入操作等同於:
std::cin >> v1;
std::cin >> v2;
我們的輸入操作的效果是從標准的輸入中讀取兩個值,存儲在v1和v2中。
完善程序
剩余的部分是輸出結果:
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
盡管這個語句比輸出提示信息的語句長一些,但是它們本質上沒有區別。它把每個操作數都打印到標准的輸出中。其中有趣的是這些值的類型是不同的。有些操作數是字符文本,例如
"The sum of "
而其它的一些是整型值,例如v1、v2和算術表達式的結果:
v1 + v2
iostream類庫定義了兩種輸入和輸出操作符,它們能夠接受所有的內建類型。
請注意:
編寫C++程序的時候,在大多數情況下出現空格的時候都可以用新行代替。這條規則的例外是字符串文本中的空格不能用新行代替。另一個例外是預處理程序指令中的空格也不允許替代。
已初始化和未初始化的變量
在C++中初始化是一個很重要的概念。初始化變量指在定義的時候給變量賦值。未初始化變量沒有賦予初始值:
int val1 = 0; // 初始化了的
int val2; // 未初始化的
給變量賦予初始值一般都是正確的,但是不一定是必要的。當我們能夠肯定第一次使用變量的時候會給它賦予一個新值,就沒有必要賦予初始值。
當我們定義變量的時候,我們必須給它賦予初始值,除非我們能夠肯定在變量使用之前這個值會被覆蓋(重載)。如果不能保證變量被讀取之前先復位,我們就應該初始化它:
練習3:
在標准輸出中打印出"Hello,World"。
練習4:
我們的程序使用內建的加操作符(+)來生成兩個數的和。編寫一個程序使用乘操作符(*)生成兩個數字的積。
練習5:
我們把輸出信息寫在一個長的語句中了。重新編寫這個程序,使用獨立的語句輸出每一個操作數。
練習6:
解釋下面的程序片段的功能:
std::cout << "The sum of " << v1;
<< " and " << v2;
<< " is " << v1 + v2
<< std::endl;
這段代碼合法嗎?如果合法,是為什麼?如果不合法,又是為什麼?