国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++ 程序設(shè)計入門

這篇具有很好參考價值的文章主要介紹了C++ 程序設(shè)計入門。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

C++ 程序設(shè)計入門

?作者簡介:人工智能專業(yè)本科在讀,喜歡計算機(jī)與編程,寫博客記錄自己的學(xué)習(xí)歷程。
??個人主頁:小嗷犬的個人主頁
??個人網(wǎng)站:小嗷犬的技術(shù)小站
??個人信條:為天地立心,為生民立命,為往圣繼絕學(xué),為萬世開太平。



程序是算法與數(shù)據(jù)結(jié)構(gòu)的載體,是計算機(jī)用以解決問題的工具。

而在程序設(shè)計比賽中,最主流的語言是 C++。

學(xué)習(xí)編程是學(xué)習(xí)程序設(shè)計最 基礎(chǔ) 的部分。

如何開始

環(huán)境配置

工欲善其事,必先利其器。

集成開發(fā)環(huán)境(IDE)

IDE 是 Integrated Development Environment 的縮寫,即集成開發(fā)環(huán)境。它包括了代碼編輯器、編譯器、調(diào)試器等程序從編寫到運(yùn)行的一系列工具。配置較為簡單,適合入門玩家。

在競賽中最常見的是 Dev-C++。Dev-C++ 的優(yōu)點(diǎn)在于界面簡潔友好,安裝便捷,支持單文件編譯,因此成為了許多入門程序設(shè)計選手以及 C++ 語言初學(xué)者的首選。

Dev-C++ 起源于 Colin Laplace 編寫的 Bloodshed Dev-C++。該版本自 2005 年 2 月 22 日停止更新。后續(xù)又有 Orwell Dev-C++、Embarcadero Dev-C++ 等衍生版本。

以上的 Dev-C++ 分發(fā)都被認(rèn)為是「官方的」。此外,在 2015 年 Orwell Dev-C++ 停止更新后,因為教學(xué)需要,一位來自中國的個人開發(fā)者 royqh1979 決定繼續(xù)開發(fā)他的 Dev-C++ 個人分支,命名為小熊貓 Dev-C++,集成了智能提示和高版本的 MinGW64,非常便于國內(nèi)的個人使用和學(xué)習(xí),項目官網(wǎng)位于 小熊貓 Dev-C++,源代碼托管于 Github。

C++ 程序設(shè)計入門

小熊貓官網(wǎng)也提供了相應(yīng)的 小熊貓 Dev-C++ 入門教程,本文推薦使用小熊貓 Dev-C++ 來進(jìn)行 C++ 的學(xué)習(xí)。

編譯器

除了使用 IDE 之外,我們也可以使用編譯器來編譯我們的代碼。這么做的好處是可以分離程序的編寫和編譯過程,使得我們可以選擇更加適合自己的代碼編輯器;同時,也可以使用命令行更靈活地編譯我們的代碼。

常見的編譯器有 GCC、Clang、MSVC 等。

配置相較于 IDE 來說稍微復(fù)雜一些,適合已經(jīng)對 C++ 有一定理解的玩家。


第一個 C++ 程序

通過編寫一個簡單的示例程序來開始我們的 C++ 之旅吧!

#include <iostream>  // 引用輸入輸出流頭文件
using namespace std; // 使用標(biāo)準(zhǔn)命名空間

int main()
{                                    // 定義 main 函數(shù)
    cout << "Hello, SWUFE!" << endl; // 輸出 Hello, SWUFE! 并換行
    return 0;                        // 返回 0 表示程序正常結(jié)束
}

與它相對應(yīng)的 C 語言版本如下:

#include <stdio.h> // 引用標(biāo)準(zhǔn)輸入輸出頭文件

int main()
{                              // 定義 main 函數(shù)
    printf("Hello, SWUFE!\n"); // 輸出 Hello, SWUFE! 并換行
    return 0;                  // 返回 0 表示程序正常結(jié)束
}

注意:本文的 C 語言代碼僅做參考,C++ 基本上兼容 C 語言,并且擁有許多新的功能,可以讓選手在賽場上事半功倍。


C++ 語法基礎(chǔ)

代碼框架

如果你不想深究背后的原理,初學(xué)時可以直接將這個「框架」背下來:

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

int main()
{
    // do something...
    return 0;
}
什么是 include

#include 其實是一個預(yù)處理命令,意思為將一個文件在這條語句處,被的文件被稱為頭文件。也就是說,在編譯時,編譯器會復(fù)制頭文件 iostream 中的內(nèi)容,粘貼#include <iostream> 這條語句處。這樣,你就可以使用 iostream 中提供的 std::cinstd::cout、std::endl 等對象了。

如果你學(xué)過 C 語言,你會發(fā)現(xiàn)目前我們接觸的 C++ 中的頭文件一般都不帶 .h 后綴,而那些 C 語言中的頭文件 xx.h 都變成了 cxx,如 stdio.h 變成了 cstdio。因為 C++ 為了和 C 保持兼容,都直接使用了 C 語言中的頭文件,為了區(qū)分 C++ 的頭文件和 C 的頭文件,使用了 c 前綴。

通常情況下,我們只需要 #include 自己所需的頭文件,比如上文的 iostreamcstdio,引入它們就可以使用 std::cin、std::cout、scanf、printf 來進(jìn)行輸入輸出了。

什么是 namespace

namespace 是命名空間的意思。C++ 的命名空間機(jī)制可以用來解決復(fù)雜項目中名字沖突的問題。

舉個例子:C++ 標(biāo)準(zhǔn)庫的所有內(nèi)容均定義在 std 命名空間中,如果你定義了一個叫 cin 的變量,則可以通過 cin 來訪問你定義的 cin 變量,通過 std::cin 訪問標(biāo)準(zhǔn)庫的 cin 對象,而不用擔(dān)心產(chǎn)生沖突。

如果你不想每次都寫 std::,可以使用 using namespace std; 來引入 std 命名空間,這樣你就可以直接使用 cin、cout、endl 等對象了。

什么是 main 函數(shù)

可以理解為程序運(yùn)行時就會執(zhí)行 main() 中的代碼。

實際上,main 函數(shù)是由系統(tǒng)或外部程序調(diào)用的。如,你在命令行中調(diào)用了你的程序,也就是調(diào)用了你程序中的 main 函數(shù)。

最后的 return 0; 表示程序運(yùn)行成功。默認(rèn)情況下,程序結(jié)束時返回 0 表示一切正常,否則返回值表示錯誤代碼。這個值返回給誰呢?其實就是調(diào)用你寫的程序的系統(tǒng)或外部程序,它會在你的程序結(jié)束時接收到這個返回值。如果不寫 return 語句的話,程序正常結(jié)束默認(rèn)返回值也是 0。

在 C 或 C++ 中,程序的返回值不為 0 會導(dǎo)致運(yùn)行時錯誤(RE)。

注釋

在 C++ 代碼中,注釋有兩種寫法:

  • 單行注釋://
  • 多行注釋:/* */

程序運(yùn)行沒有影響,可以用來解釋程序的意思,還可以在讓某段代碼不執(zhí)行(但是依然保留在源文件里)。

在工程開發(fā)中,注釋可以便于日后維護(hù)、他人閱讀。

在程序設(shè)計比賽中,很少有人寫大段的注釋,但注釋可以便于在寫代碼的時候理清思路,或者便于日后復(fù)習(xí)。而且,如果要寫題解、教程的話,適量的注釋可以便于讀者閱讀,理解代碼的意圖。

因此,寫注釋是一個好習(xí)慣。

輸入與輸出

cin 與 cout

在 C++ 中,我們可以使用 cincout 來進(jìn)行輸入輸出。

#include <iostream>
using namespace std;

int main()
{
    int x, y;          // 定義兩個 int 類型的變量 x 和 y
    cin >> x >> y;     // 輸入 x 和 y
    cout << y << endl  // 輸出 y 換行
         << x << endl; // 輸出 x 換行
    return 0;
}

上面程序中,cincout 分別是輸入流和輸出流的對象,>><< 分別是輸入運(yùn)算符和輸出運(yùn)算符。

如何理解輸入輸出流呢?我們可以把數(shù)據(jù)流想象成水流,cin 是一個水龍頭,cout 是一個排水口,變量 xy 是兩個容器,>><< 是連接水龍頭和容器、排水口和容器的管道。cin >> x 的意思就是將水龍頭的水流輸入到容器 x 中,cout << y 的意思就是將容器 y 中的水流輸出到排水口,運(yùn)算符的方向總是指向數(shù)據(jù)流的方向。

事實上,上述的比喻并不嚴(yán)謹(jǐn),因為變量是有多種類型的,這意味著“容器”也有多種類型,自然數(shù)據(jù)流中就不會只有水這一種“液體”。

關(guān)于變量的知識,我們將在后面的章節(jié)中進(jìn)行介紹。

scanf 與 printf

scanfprintf 其實是 C 語言提供的函數(shù)。大多數(shù)情況下,它們的速度比 cincout 更快,并且能夠方便地控制輸入輸出格式。

#include <cstdio>

int main()
{
    int x, y;
    scanf("%d%d", &x, &y);    // 輸入 x 和 y
    printf("%d\n%d\n", y, x); // 輸出 y 換行再輸出 x 換行
    return 0;
}
// 這段代碼與相同功能的 C 語言代碼只有頭文件的差別

其中,%d 表示讀入/輸出的變量是一個有符號整型(int 型)的變量。這樣的符號稱為格式控制符,用來控制輸入輸出的格式。

常用的控制符有:

控制符 說明
%s 字符串
%c 字符
%d 有符號十進(jìn)制整數(shù) (int)
%lld%I64d (依系統(tǒng)而定) 長整型 (long long)
%lf 雙精度浮點(diǎn)數(shù) (double)
%u 無符號十進(jìn)制整數(shù) (unsigned int)
%llu%I64u (依系統(tǒng)而定) 無符號長整型 (unsigned long long)

格式控制符除了能表示類型外,還能夠控制格式,常用的有:

控制符 說明
%1d 長度為 1 的整型。
在讀入時,即使沒有空格也可以逐位讀入數(shù)字。
在輸出時,若指定的長度大于數(shù)字的位數(shù),就會在數(shù)字前用空格填充。若指定的長度小于數(shù)字的位數(shù),就沒有效果。
%.6lf 用于輸出,保留 6 位小數(shù)。

上面兩個格式控制符相應(yīng)位置填入不同數(shù)字就會有不同的效果,如 %.3lf 表示保留 3 位小數(shù)。

擴(kuò)展內(nèi)容

endl 與 ‘\n’

endl 是 C++ 中的一個 IO 操作符,它的作用是插入一個換行符,并刷新輸出緩沖區(qū)。cout << endl; 事實上等價于 cout << '\n' << flush;。

大部分情況下 cout << '\n'cout << endl 運(yùn)行起來并無明顯區(qū)別,除非題目要求你輸出之后立即刷新緩沖區(qū)(說的就是你,交互題!)。

C++ 中的空白字符

在 C++ 中,所有空白字符(空格、制表符、換行),多個或是單個,都被視作是一樣的。(當(dāng)然,引號中視作字符串的一部分的不算。)

因此,你可以自由地使用任何代碼風(fēng)格(除了行內(nèi)注釋、字符串字面量與預(yù)處理命令必須在單行內(nèi)),例如:

#include <iostream>

 int 

    main(){
int/**/x, y;  std::cin
>> x >>y;
                std::cout <<
          y  <<std::endl   
     << x            << std::endl

          ;

    return       0;     }

這段程序和上文寫的程序一樣,能夠完成相同的事情,讀入兩個整數(shù),然后先輸出第二個整數(shù),再輸出第一個整數(shù),每輸出一個整數(shù)換一行。

當(dāng)然,這么做是不被推薦的。

define 命令

#define 是一種預(yù)處理命令,用于定義宏,本質(zhì)上是文本替換。例如:

#include <iostream>
#define n 666

// n 不是變量,而是編譯器會將代碼中所有 n 文本替換為 666,但是作為標(biāo)識符一部分的
// n 的就不會被替換,如 fn 不會被替換成 f666,同樣,字符串內(nèi)的也不會被替換

int main()
{
    std::cout << n; // 輸出 666
    return 0;
}

宏可以帶參數(shù),帶參數(shù)的宏可以像函數(shù)一樣使用:

#include <iostream>
#define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))

int main()
{
    std::cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << std::endl; // 輸出 3 16
}

但是帶參數(shù)的宏和函數(shù)有區(qū)別。因為宏是文本替換,所以會引發(fā)許多問題。如:

#include <iostream>
#define sum(x, y) x + y
// 這里應(yīng)當(dāng)為 #define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))

int main()
{
    std::cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << std::endl;
    // 輸出為 3 11,因為 #define 是文本替換,后面的語句被替換為了 2* 3 + 5
    int i = 1;
    std::cout << square(++i) << ' ' << i;
    // 輸出未定義,因為 ++i 被執(zhí)行了兩遍
    // 而同一個語句中多次修改同一個變量是未定義行為(有例外)
}

因此,使用 #define 是有風(fēng)險的,應(yīng)謹(jǐn)慎使用


變量

數(shù)據(jù)類型

C++ 的類型系統(tǒng)主要由如下幾部分組成:

  • 基礎(chǔ)類型(括號內(nèi)為代表關(guān)鍵詞/代表類型)
    • 無類型/void 型 (void)
    • (C++11 起)空指針類型 (std::nullptr_t)
    • 算術(shù)類型
      • 整數(shù)類型 (int)
      • 布爾類型/bool 型 (bool)
      • 字符類型 (char)
      • 浮點(diǎn)類型 (float, double)
  • 復(fù)合類型1
布爾類型

一個 bool 類型的變量取值只可能為兩種:truefalse。

一般情況下,一個 bool 類型變量占有 1 字節(jié)(一般情況下,1 字節(jié) = 8 位)的空間。

整數(shù)類型

用于存儲整數(shù),最基礎(chǔ)的整數(shù)類型是 int。

整數(shù)類型一般按位寬有 5 個梯度:char, short, int, long, long long。

C++ 標(biāo)準(zhǔn)保證 1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long),但對于每種類型的位寬并沒有做出具體的規(guī)定。

對于 int 關(guān)鍵字,可以使用如下修飾關(guān)鍵字進(jìn)行修飾:

  • 符號性
    • signed:有符號的(默認(rèn))
    • unsigned:無符號的
  • 大小
    • short:短整型
    • long:長整型
    • long long:長長整型

下表給出了各個類型的等價形式和位寬:

類型 等價形式 位寬(C++ 標(biāo)準(zhǔn)) 位寬(常見)
signed char signed char 8 8 8
unsigned char unsigned char 8 8 8
short, short int, signed short, signed short int short int ≥ 16 \geq 16 16 16 16 16
unsigned short, unsigned short int unsigned short int ≥ 16 \geq 16 16 16 16 16
int, signed, signed int int ≥ 16 \geq 16 16 32 32 32
unsigned, unsigned int unsigned int ≥ 16 \geq 16 16 32 32 32
long, long int, signed long, signed long int long int ≥ 32 \geq 32 32 32 32 32
unsigned long, unsigned long int unsigned long int ≥ 32 \geq 32 32 32 32 32
long long, long long int, signed long long, signed long long int long long int ≥ 64 \geq 64 64 64 64 64
unsigned long long, unsigned long long int unsigned long long int ≥ 64 \geq 64 64 64 64 64

對于整數(shù)類型,當(dāng)位寬為 x x x 時,有符號類型的表示范圍為 ? 2 x ? 1 ~ 2 x ? 1 ? 1 -2^{x-1} \sim 2^{x-1}-1 ?2x?12x?1?1,無符號類型的表示范圍為 0 ~ 2 x ? 1 0 \sim 2^x-1 02x?1。

程序設(shè)計中最常用的兩個整數(shù)類型是 intlong long

字符類型

分為「窄字符類型」和「寬字符類型」,由于程序設(shè)計競賽幾乎不會用到寬字符類型,故此處僅介紹窄字符類型。

窄字符型位數(shù)一般為 8 位,實際上底層存儲方式仍然是整數(shù),一般通過 ASCII 編碼 實現(xiàn)字符與整數(shù)的一一對應(yīng),有如下三種:

  • signed char:有符號字符表示的類型,表示范圍在 ? 128 ~ 127 -128 \sim 127 ?128127 之間。
  • unsigned char:無符號字符表示的類型,表示范圍在 0 ~ 255 0 \sim 255 0255 之間。
  • char:有符號或無符號字符表示的類型,具體由編譯器決定。

事實上,只有 char 類型的變量才應(yīng)該用于表示字符,signed charunsigned char 類型通常被視為整數(shù)類型。

浮點(diǎn)類型

用于存儲「實數(shù)」(注意并不是嚴(yán)格意義上的實數(shù),而是實數(shù)在一定規(guī)則下的近似),包括以下三種:

  • float:單精度浮點(diǎn)類型。如果支持就會匹配 IEEE-754 binary32 格式。
  • double:雙精度浮點(diǎn)類型。如果支持就會匹配 IEEE-754 binary64 格式。
  • long double:擴(kuò)展精度浮點(diǎn)類型。如果支持就會匹配 IEEE-754 binary128 格式,否則如果支持就會匹配 IEEE-754 binary64 擴(kuò)展格式,否則匹配某種精度優(yōu)于 binary64 而值域至少和 binary64 一樣好的非 IEEE-754 擴(kuò)展浮點(diǎn)格式,否則匹配 IEEE-754 binary64 格式。
浮點(diǎn)格式 位寬 最大正數(shù) 精度位數(shù)
IEEE-754 binary32 格式 32 32 32 3.4 × 1 0 38 3.4 \times 10^{38} 3.4×1038 6 ~ 9 6 \sim 9 69
IEEE-754 binary64 格式 64 64 64 1.8 × 1 0 308 1.8 \times 10^{308} 1.8×10308 15 ~ 17 15 \sim 17 1517
IEEE-754 binary64 擴(kuò)展格式 ≥ 80 \geq 80 80 ≥ 1.2 × 1 0 4932 \geq 1.2 \times 10^{4932} 1.2×104932 ≥ 18 ~ 21 \geq 18 \sim 21 1821
IEEE-754 binary128 格式 128 128 128 ≥ 1.2 × 1 0 4932 \geq 1.2 \times 10^{4932} 1.2×104932 33 ~ 36 33 \sim 36 3336

IEEE-754 浮點(diǎn)格式的最小負(fù)數(shù)是最大正數(shù)的相反數(shù)。

因為 float 類型表示范圍較小,且精度不高,實際應(yīng)用中常使用 double 類型表示浮點(diǎn)數(shù)。

除此之外,浮點(diǎn)類型可以支持一些特殊值:

  • 正負(fù)無窮:INFINITY / -INFINITY
  • 負(fù)零:-0.0,例如 1.0 / 0.0 == INFINITY,1.0 / -0.0 == -INFINITY
  • 非數(shù) (NaN):std::nan, NAN, 一般可以由 0.0 / 0.0 之類的運(yùn)算產(chǎn)生。它與任何值(包括自身)比較都不相等,C++11 后可以 使用 std::isnan 判斷一個浮點(diǎn)數(shù)是不是 NaN
無類型

void 類型為無類型,與上面幾種類型不同的是,不能將一個變量聲明為 void 類型。但是函數(shù)的返回值允許為 void 類型,表示該函數(shù)無返回值。

類型轉(zhuǎn)換

在一些時候(比如某個函數(shù)接受 int 類型的參數(shù),但傳入了 double 類型的變量),我們需要將某種類型,轉(zhuǎn)換成另外一種類型。

C++ 中類型的轉(zhuǎn)換機(jī)制較為復(fù)雜,這里主要介紹對于基礎(chǔ)數(shù)據(jù)類型的兩種轉(zhuǎn)換:數(shù)值提升和數(shù)值轉(zhuǎn)換。

數(shù)值提升

數(shù)值提升過程中,值本身保持不變,不會產(chǎn)生精度損失。

整數(shù)提升

小整數(shù)類型(如 char)的純右值2可轉(zhuǎn)換成較大整數(shù)類型(如 int)的純右值。

具體而言,算術(shù)運(yùn)算符不接受小于 int 的類型作為它的實參,而在左值到右值轉(zhuǎn)換后,如果適用就會自動實施整數(shù)提升。

具體地,有如下規(guī)則:

  • 源類型為 signed char、signed short / short 時,可提升為 int
  • 源類型為 unsigned char、unsigned short 時,若 int 能保有源類型的值范圍,則可提升為 int,否則可提升為 unsigned int。
  • char 的提升規(guī)則取決于其底層類型是 signed char 還是 unsigned char
  • bool 類型可轉(zhuǎn)換到 intfalse 變?yōu)?0true 變?yōu)?1。
  • 若目標(biāo)類型的值范圍包含源類型,且源類型的值范圍不能被 intunsigned int 包含,則源類型可提升為目標(biāo)類型3
浮點(diǎn)提升

位寬較小的浮點(diǎn)數(shù)可以提升為位寬較大的浮點(diǎn)數(shù)(例如 float 類型的變量和 double 類型的變量進(jìn)行算術(shù)運(yùn)算時,會將 float 類型變量提升為 double 類型變量),其值不變。

數(shù)值轉(zhuǎn)換

數(shù)值轉(zhuǎn)換過程中,值可能會發(fā)生改變,可能會產(chǎn)生精度損失。

注意:數(shù)值提升優(yōu)先于數(shù)值轉(zhuǎn)換。如 bool->int 時是數(shù)值提升而非數(shù)值轉(zhuǎn)換。

整數(shù)轉(zhuǎn)換
  • 如果目標(biāo)類型為位寬為 x x x 的無符號整數(shù)類型,則轉(zhuǎn)換結(jié)果是原值 ? m o d ? 2 x \bmod 2^x mod2x 后的結(jié)果。
    • 若目標(biāo)類型位寬大于源類型位寬:
      • 若源類型為有符號類型,一般情況下需先進(jìn)行符號位擴(kuò)展再轉(zhuǎn)換。
      • 若源類型為無符號類型,則需先進(jìn)行零擴(kuò)展再轉(zhuǎn)換。
    • 若目標(biāo)類型位寬不大于源類型位寬,則需先截斷再轉(zhuǎn)換。
  • 如果目標(biāo)類型為位寬為 x x x 的帶符號整數(shù)類型,則 一般情況下,轉(zhuǎn)換結(jié)果可以認(rèn)為是原值 ? m o d ? 2 x \bmod 2^x mod2x 后的結(jié)果。
  • 如果目標(biāo)類型是 bool,則是 布爾轉(zhuǎn)換。
  • 如果源類型是 bool,則 false 轉(zhuǎn)為對應(yīng)類型的 0,true 轉(zhuǎn)為對應(yīng)類型的 1。
浮點(diǎn)轉(zhuǎn)換

位寬較大的浮點(diǎn)數(shù)轉(zhuǎn)換為位寬較小的浮點(diǎn)數(shù),會將該數(shù)舍入到目標(biāo)類型下最接近的值。

浮點(diǎn)整數(shù)轉(zhuǎn)換
  • 浮點(diǎn)數(shù)轉(zhuǎn)換為整數(shù)時,會舍棄浮點(diǎn)數(shù)的全部小數(shù)部分。
    如果目標(biāo)類型是 bool,則是 布爾轉(zhuǎn)換
  • 整數(shù)轉(zhuǎn)換為浮點(diǎn)數(shù)時,會舍入到目標(biāo)類型下最接近的值。
    如果該值不能適應(yīng)到目標(biāo)類型中,那么行為未定義。
    如果源類型是 bool,那么 false 轉(zhuǎn)換為 0,而 true 轉(zhuǎn)換為 1
布爾轉(zhuǎn)換

將其他類型轉(zhuǎn)換為 bool 類型時,零值轉(zhuǎn)換為 false,非零值轉(zhuǎn)換為 true。

定義變量

簡單來說4,定義一個變量,需要包含類型說明符(指明變量的類型),以及要定義的變量名。

例如,下面這幾條語句都是變量定義語句:

int hello;
double swufe;
char acm = '6';

在目前我們所接觸到的程序段中,定義在花括號內(nèi)部的變量是局部變量,而定義在所有花括號外部的變量是全局變量。實際有例外,但是現(xiàn)在不必了解。

定義時沒有初始化值的全局變量會被初始化為 0。而局部變量沒有這種特性,需要手動賦初始值,否則可能引起難以發(fā)現(xiàn)的 bug。

變量作用域

變量的作用域是指變量在程序中有效的范圍。

  • 全局變量的作用域:自其定義之處開始,至文件結(jié)束位置為止。
  • 局部變量的作用域:自其定義之處開始,至代碼塊結(jié)束位置為止。

由一對大括號括起來的若干語句構(gòu)成一個代碼塊。

int x = 6; // 定義全局變量

int main()
{
    int x = 555;       // 定義局部變量
    printf("%d\n", x); // 輸出 x
    return 0;
}

如果一個代碼塊的內(nèi)嵌塊中定義了相同變量名的變量,則內(nèi)層塊中將無法訪問外層塊中相同變量名的變量。

例如上面的代碼中,輸出的 x 的值將是 555。因此為了防止出現(xiàn)意料之外的錯誤,請盡量避免局部變量與全局變量重名的情況。

常量

常量,又稱常變量,值為固定值,在程序執(zhí)行期間不會改變。

常量的值在定義后不能被修改。定義時加一個 const 關(guān)鍵字即可。

const double Pi = 3.1415926;
Pi = 4;

如果修改了常量的值,在編譯環(huán)節(jié)就會報錯:error: assignment of read-only variable‘Pi’


運(yùn)算

算術(shù)運(yùn)算符

運(yùn)算符 功能
+ (單目) 取正
- (單目) 取負(fù)
* 乘法
/ 除法
% 取模
+ 加法
- 減法

單目運(yùn)算符(又稱一元運(yùn)算符)指被操作對象只有一個的運(yùn)算符,而雙目運(yùn)算符(又稱二元運(yùn)算符)的被操作對象有兩個。例如 1 + 2 中加號就是雙目運(yùn)算符,它有 12 兩個被操作數(shù)。

算術(shù)運(yùn)算符中有兩個單目運(yùn)算符(取正、取負(fù))以及五個雙目運(yùn)算符(乘法、除法、取模、加法、減法),其中單目運(yùn)算符的優(yōu)先級最高。

其中取模運(yùn)算符 % 意為計算兩個整數(shù)相除得到的余數(shù),即求余數(shù)。

C++ 中的算術(shù)運(yùn)算遵循數(shù)學(xué)中加減乘除的優(yōu)先規(guī)律,首先進(jìn)行優(yōu)先級高的運(yùn)算,同優(yōu)先級自左向右運(yùn)算,括號提高優(yōu)先級。

算術(shù)運(yùn)算中的類型轉(zhuǎn)換

對于雙目算術(shù)運(yùn)算符,當(dāng)參與運(yùn)算的兩個變量類型相同時,不發(fā)生類型轉(zhuǎn)換,運(yùn)算結(jié)果將會用參與運(yùn)算的變量的類型容納,否則會發(fā)生類型轉(zhuǎn)換,以使兩個變量的類型一致。

轉(zhuǎn)換的規(guī)則如下:

  • 先將 char、bool、short 等類型提升至 int(或 unsigned int,取決于原類型的符號性)類型;
  • 若存在一個變量類型為 long double,會將另一變量轉(zhuǎn)換為 long double 類型;
  • 否則,若存在一個變量類型為 double,會將另一變量轉(zhuǎn)換為 double 類型;
  • 否則,若存在一個變量類型為 float,會將另一變量轉(zhuǎn)換為 float 類型;
  • 否則(即參與運(yùn)算的兩個變量均為整數(shù)類型):
    • 若兩個變量符號性一致,則將位寬較小的類型轉(zhuǎn)換為位寬較大的類型;
    • 否則,若無符號變量的位寬不小于帶符號變量的位寬,則將帶符號數(shù)轉(zhuǎn)換為無符號數(shù)對應(yīng)的類型;
    • 否則,若帶符號操作數(shù)的類型能表示無符號操作數(shù)類型的所有值,則將無符號操作數(shù)轉(zhuǎn)換為帶符號操作數(shù)對應(yīng)的類型;
    • 否則,將帶符號數(shù)轉(zhuǎn)換為相對應(yīng)的無符號類型。

例如,對于一個整型 int 變量 x 和另一個雙精度浮點(diǎn)型 double 類型變量 y

  • x/5 的結(jié)果將會是整型 int;
  • x/5.0 的結(jié)果將會是雙精度浮點(diǎn)型 double;
  • x/y 的結(jié)果將會是雙精度浮點(diǎn)型 double;
  • x*2/5 的結(jié)果將會是整型 int;
  • x*2.0/5 的結(jié)果將會是雙精度浮點(diǎn)型 double

位運(yùn)算符

運(yùn)算符 功能
~ (單目) 按位非
& 按位與
| 按位或
^ 按位異或
<< 按位左移
>> 按位右移

位運(yùn)算就是基于整數(shù)的二進(jìn)制表示進(jìn)行的運(yùn)算。由于計算機(jī)內(nèi)部就是以二進(jìn)制來存儲數(shù)據(jù),位運(yùn)算是相當(dāng)快的。

需要注意的是,位運(yùn)算符的優(yōu)先級低于普通的算數(shù)運(yùn)算符。

自增/自減 運(yùn)算符

有時我們需要讓變量進(jìn)行增加 1(自增)或者減少 1(自減),這時自增運(yùn)算符 ++ 和自減運(yùn)算符 -- 就派上用場了。

自增/自減運(yùn)算符可放在變量前或變量后面,在變量前稱為前綴,在變量后稱為后綴,單獨(dú)使用時前綴后綴無需特別區(qū)別,如果需要用到表達(dá)式的值則需注意,具體可看下面的例子。

i = 10;
x1 = i++;  // x1 = 10。先 x1 = i,然后 i = i + 1

i = 10;
x2 = ++i;  // x2 = 11。先 i = i + 1,然后賦值 x2

i = 10;
x3 = i--;  // x3 = 10。先賦值 x3,然后 i = i - 1

i = 10;
x4 = --i;  // x4 = 9。先 i = i - 1,然后賦值 x4

復(fù)合賦值運(yùn)算符

復(fù)合賦值運(yùn)算符實際上是表達(dá)式的縮寫形式??煞譃閺?fù)合算術(shù)運(yùn)算符 +=、-=、*=/=、%= 和復(fù)合位運(yùn)算符 &=|=、^=、<<=>>=。

例如,x = x + 2 可寫為 x += 2,x = x - 2 可寫為 x -= 2x = x * 2 可寫為 x *= 2。

條件運(yùn)算符

條件運(yùn)算符可以看作 if 語句的簡寫,a ? b : c 中如果表達(dá)式 a 成立,那么這個條件表達(dá)式的結(jié)果是 b,否則條件表達(dá)式的結(jié)果是 c。

條件運(yùn)算符是 C++ 中唯一的三目運(yùn)算符,所以平常說的三目運(yùn)算符就是指條件運(yùn)算符。

比較運(yùn)算符

運(yùn)算符 功能
> 大于
>= 大于等于
< 小于
<= 小于等于
== 等于
!= 不等于

其中特別需要注意的是要將等于運(yùn)算符 == 和賦值運(yùn)算符 = 區(qū)分開來,這在判斷語句中尤為重要。

if (x=1)if (x==1) 看起來類似,但實際功能卻相差甚遠(yuǎn)。第一條語句是在對 x 進(jìn)行賦值,若賦值為非 0 時為真值,表達(dá)式的條件始終是滿足的,無法達(dá)到判斷的作用;而第二條語句才是對 x 的值進(jìn)行判斷。

邏輯運(yùn)算符

運(yùn)算符 功能
&& 邏輯與
|| 邏輯或
! 邏輯非
c = a && b;  // 當(dāng) a 與 b 都為真時則 c 為真
c = a || b;  // 當(dāng) a 或 b 其中一個為真時則 c 為真
c = !a;  // 當(dāng) a 為假時則 c 為真

逗號運(yùn)算符

逗號運(yùn)算符可將多個表達(dá)式分隔開來,被分隔開的表達(dá)式按從左至右的順序依次計算,整個表達(dá)式的值是最后的表達(dá)式的值。逗號表達(dá)式的優(yōu)先級在所有運(yùn)算符中的優(yōu)先級是 最低 的。

x1, x2, x3;  // 最后的值為 x3 的運(yùn)算結(jié)果。

x = 1 + 2, 3 + 4, 5 + 6;  //得到 x 的值為 3 而不是 11
// 因為賦值運(yùn)算符 "=" 的優(yōu)先級比逗號運(yùn)算符高,先進(jìn)行了賦值運(yùn)算才進(jìn)行逗號運(yùn)算。

x = (1 + 2, 3 + 4, 5 + 6);  // 得到 x 的值為 11
// 若要讓 x 的值得到逗號運(yùn)算的結(jié)果,則應(yīng)將整個表達(dá)式用括號提高優(yōu)先級。

成員訪問運(yùn)算符

運(yùn)算符 功能
[] 數(shù)組下標(biāo)
. 對象成員
& (單目) 取地址/獲取引用
* (單目) 間接尋址/解引用
-> 指針成員

這些運(yùn)算符用來訪問對象的成員或者內(nèi)存。這里還省略了兩個很少用到的運(yùn)算符 .*->* ,其具體用法可以參見 C++ 語言手冊。

auto result1 = arr[1];  // 獲取 arr 中下標(biāo)為 1 的對象
auto result2 = p.q;     // 獲取 p 對象的 q 成員
auto result3 = p -> q;  // 獲取 p 指針指向的對象的 q 成員,等價于 (*p).q
auto result4 = &v;      // 獲取指向 v 的指針
auto result5 = *v;      // 獲取 v 指針指向的對象

C++ 運(yùn)算符優(yōu)先級總表

來自 C++ 運(yùn)算符優(yōu)先級 - cppreference,有修改。

優(yōu)先級 運(yùn)算符 描述 結(jié)合性
1 :: 作用域解析 從左到右
2 ++
--
type()、type{}
f()
a[]
.、->
后綴自增
后綴自減
函數(shù)風(fēng)格轉(zhuǎn)型
函數(shù)調(diào)用
下標(biāo)訪問
成員訪問
從左到右
3 ++a
--a
+a
-a
!
~
(type)
*a
&a
sizeof
new
new[]
delete
delete[]
前綴自增
前綴自減
取正
取負(fù)
邏輯非
按位非
C 風(fēng)格轉(zhuǎn)型
間接尋址/解引用
取地址/獲取引用
返回類型大小
動態(tài)元素類型分配
動態(tài)數(shù)組類型分配
動態(tài)析構(gòu)元素內(nèi)存
動態(tài)析構(gòu)數(shù)組內(nèi)存
從右到左
4 .*
->*
類對象成員引用
類指針成員引用
從左到右
5 *
/
%
乘法
除法
取模
從左到右
6 +
-
加法
減法
從左到右
7 <<
>>
左移
右移
從左到右
8 <=> 三路比較 從左到右
9 <
<=
>
>=
小于
小于等于
大于
大于等于
從左到右
10 ==
!=
等于
不等于
從左到右
11 & 按位與 從左到右
12 ^ 按位異或 從左到右
13 | 按位或 從左到右
14 && 邏輯與 從左到右
15 || 邏輯或 從左到右
16 ?:
throw
=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
條件運(yùn)算符
拋出異常
賦值
加賦值
減賦值
乘賦值
除賦值
取模賦值
左移賦值
右移賦值
按位與賦值
按位異或賦值
按位或賦值
從右到左
17 , 逗號 從左到右

流程控制

分支結(jié)構(gòu)

一個程序默認(rèn)是按照代碼的順序執(zhí)行下來的,有時我們需要選擇性的執(zhí)行某些語句,這時候就需要分支的功能來實現(xiàn)。選擇合適的分支語句可以提高程序的效率。

if 語句
基本 if 語句
if (條件)
{
    主體;
}

if 語句通過對條件進(jìn)行求值,若結(jié)果為真(非 0),執(zhí)行語句,否則不執(zhí)行。

如果主體中只有單個語句的話,花括號可以省略。

if…else 語句
if (條件)
{
    主體1;
}
else
{
    主體2;
}

if…else 語句和 if 語句類似,else 不需要再寫條件。當(dāng) if 語句的條件滿足時會執(zhí)行 if 里的語句,if 語句的條件不滿足時會執(zhí)行 else 里的語句。同樣,當(dāng)主體只有一條語句時,可以省略花括號。

else if 語句
if (條件1)
{
    主體1;
}
else if (條件2)
{
    主體2;
}
else if (條件3)
{
    主體3;
}
else
{
    主體4;
}

else if 語句是 if 和 else 的組合,對多個條件進(jìn)行判斷并選擇不同的語句分支。在最后一條的 else 語句不需要再寫條件。例如,若條件 1 為真,執(zhí)行主體 1,條件 3 為真而條件 1 和條件 2 都為假,執(zhí)行主體 3,所有的條件都為假才執(zhí)行主體 4。

實際上,這一個語句相當(dāng)于第一個 if 的 else 分句只有一個 if 語句,就將花括號省略之后放在一起了。如果條件相互之間是并列關(guān)系,這樣寫可以讓代碼的邏輯更清晰。

switch 語句
switch (選擇句)
{
case 標(biāo)簽1:
    主體1;
case 標(biāo)簽2:
    主體2;
default:
    主體3;
}

switch 語句執(zhí)行時,先求出選擇句的值,然后根據(jù)選擇句的值選擇相應(yīng)的標(biāo)簽,從標(biāo)簽處開始執(zhí)行。其中,選擇句必須是一個整數(shù)類型表達(dá)式,而標(biāo)簽都必須是整數(shù)類型的常量。例如:

int i = 1; // 這里的 i 的數(shù)據(jù)類型是整型 ,滿足整數(shù)類型的表達(dá)式的要求

switch (i)
{
case 1:
    cout << "SWUFE" << endl;
}
char i = 'A';

// 這里的 i 的數(shù)據(jù)類型是字符型 ,但 char
// 也是屬于整數(shù)的類型,滿足整數(shù)類型的表達(dá)式的要求
switch (i)
{
case 'A':
    cout << "SWUFE" << endl;
}

switch 語句中還要根據(jù)需求加入 break 語句進(jìn)行中斷,否則在對應(yīng)的 case 被選擇之后接下來的所有 case 里的語句和 default 里的語句都會被運(yùn)行。具體例子可看下面的示例。

char i = 'B';

switch (i)
{
case 'A':
    cout << "SWUFE" << endl;
    break;

case 'B':
    cout << "ACM" << endl;

default:
    cout << "Hello World" << endl;
}

以上代碼運(yùn)行后輸出的結(jié)果為 ACMHello World,如果不想讓下面分支的語句被運(yùn)行就需要 break 了,具體例子可看下面的示例。

char i = 'B';

switch (i)
{
case 'A':
    cout << "SWUFE" << endl;
    break;

case 'B':
    cout << "ACM" << endl;
    break;

default:
    cout << "Hello World" << endl;
}

以上代碼運(yùn)行后輸出的結(jié)果為 ACM,因為 break 的存在,接下來的語句就不會繼續(xù)被執(zhí)行了。最后一個語句不需要 break,因為下面沒有語句了。

處理入口編號不能重復(fù),但可以顛倒。也就是說,入口編號的順序不重要。各個 case(包括 default)的出現(xiàn)次序可任意。例如:

char i = 'B';

switch (i)
{
case 'B':
    cout << "ACM" << endl;
    break;

default:
    cout << "Hello World" << endl;
    break;

case 'A':
    cout << "SWUFE" << endl;
}

switch 的 case 分句中也可以選擇性的加花括號。不過要注意的是,如果需要在 switch 語句中定義變量,花括號是必須要加的。例如:

char i = 'B';

switch (i)
{
case 'A':
{
    int i = 1, j = 2;
    cout << "SWUFE" << endl;
    ans = i + j;
    break;
}

case 'B':
{
    int x = 3;
    cout << "ACM" << endl;
    ans = x * x;
    break;
}

default:
{
    cout << "Hello World" << endl;
}
}

循環(huán)結(jié)構(gòu)

有時,我們需要做一件事很多遍,為了不寫過多重復(fù)的代碼,我們需要循環(huán)。

有時,循環(huán)的次數(shù)不是一個常量,那么我們無法將代碼重復(fù)多遍,必須使用循環(huán)。

for 循環(huán)
for (初始化; 判斷條件; 更新)
{
    循環(huán)體;
}

流程圖:

C++ 程序設(shè)計入門

例如:

// 讀入 n 個數(shù)
for (int i = 0; i < n; ++i)
{
    cin >> a[i];
}

for 循環(huán)的三個部分中,任何一個部分都可以省略。其中,若省略了判斷條件,相當(dāng)于判斷條件永遠(yuǎn)為真。

while 循環(huán)
while (判斷條件)
{
    循環(huán)體;
}

流程圖:

C++ 程序設(shè)計入門

例如:

// 驗證科拉茨猜想
while (x > 1)
{
    if (x % 2 == 1)
    {
        x = 3 * x + 1;
    }
    else
    {
        x = x / 2;
    }
}
do…while 循環(huán)
do
{
    循環(huán)體;
} while (判斷條件);

流程圖:

C++ 程序設(shè)計入門

與 while 語句的區(qū)別在于,do…while 語句是先執(zhí)行循環(huán)體再進(jìn)行判斷的。

// 枚舉排列
do
{
    // do someting...
} while (next_permutation(a + 1, a + n + 1));
三種循環(huán)的聯(lián)系
// for 循環(huán)
for (statement1; statement2; statement3)
{
    statement4;
}

// while 循環(huán)
statement1;
while (statement2)
{
    statement4;
    statement3;
}

在 statement4 中沒有 continue 語句(見下文)的時候是等價的,但是下面一種方法很少用到。

// while 循環(huán)
statement1;
while (statement2)
{
    statement1;
}

// do...while 循環(huán)
do
{
    statement1;
} while (statement2);

在 statement1 中沒有 continue 語句的時候這兩種方式也也是等價的。

while (1)
{
    // do something...
}

for (;;)
{
    // do something...
}

do
{
    // do something...
} while (1);

這三種方式都是永遠(yuǎn)循環(huán)下去,可以使用 break(見下文)退出。

可以看出,三種循環(huán)可以彼此代替,但一般來說,循環(huán)的選用遵守以下原則:

  1. 循環(huán)過程中有個固定的增加步驟(最常見的是枚舉)時,使用 for 循環(huán);
  2. 只確定循環(huán)的終止條件時,使用 while 循環(huán);
  3. 使用 while 循環(huán)時,若要先執(zhí)行循環(huán)體再進(jìn)行判斷,使用 do…while 循環(huán)。一般很少用到,常用場景是用戶輸入。
break 與 continue 語句

break 語句的作用是退出循環(huán)。

continue 語句的作用是跳過循環(huán)體的余下部分。下面以 continue 語句在 do…while 語句中的使用為例:

do
{
    // do something...
    continue; // 等價于 goto END;
// do something...
END:;
} while (statement);

breakcontinue 語句均可在三種循環(huán)語句的循環(huán)體中使用。

一般來說,breakcontinue 語句用于讓代碼的邏輯更加清晰,例如:

// 邏輯較為不清晰,大括號層次復(fù)雜
for (int i = 1; i <= n; ++i)
{
    if (i != x)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (j != x)
            {
                // do something...
            }
        }
    }
}

// 邏輯更加清晰,大括號層次簡單明了
for (int i = 1; i <= n; ++i)
{
    if (i == x)
        continue;
    for (int j = 1; j <= n; ++j)
    {
        if (j == x)
            continue;
        // do something...
    }
}
// for 語句判斷條件復(fù)雜,沒有體現(xiàn)「枚舉」的本質(zhì)
for (int i = l; i <= r && i % 10 != 0; ++i)
{
    // do something...
}

// for 語句用于枚舉,break 用于「到何時為止」
for (int i = l; i <= r; ++i)
{
    if (i % 10 == 0)
        break;
    // do something...
}
// 語句重復(fù),順序不自然
statement1;
while (statement3)
{
    statement2;
    statement1;
}

// 沒有重復(fù)語句,順序自然
while (1)
{
    statement1;
    if (!statement3)
        break;
    statement2;
}

高級數(shù)據(jù)類型

數(shù)組

數(shù)組是存放相同類型對象的容器,數(shù)組中存放的對象沒有名字,而是要通過其所在的位置訪問。數(shù)組的大小是固定的,不能隨意改變數(shù)組的長度。

定義數(shù)組

數(shù)組的聲明形如 a[n],其中,a 是數(shù)組的名字,n 是數(shù)組中元素的個數(shù)。在編譯時,n 應(yīng)該是已知的,也就是說,n 應(yīng)該是一個整型的常量表達(dá)式。

unsigned int n1 = 42;
const int n2 = 42;
int arr1[n1];  // 錯誤:n1 不是常量表達(dá)式
int arr2[n2];  // 正確:arr2 是一個長度為 42 的數(shù)組

不能將一個數(shù)組直接賦值給另一個數(shù)組:

int arr1[3];
int arr2 = arr1;  // 錯誤
arr2 = arr1;      // 錯誤

應(yīng)該盡量將較大的數(shù)組定義為全局變量。因為局部變量會被創(chuàng)建在棧區(qū)中,過大(大于棧的大?。┑臄?shù)組會爆棧,進(jìn)而導(dǎo)致 RE。如果將數(shù)組聲明在全局作用域中,就會在靜態(tài)區(qū)中創(chuàng)建數(shù)組。

訪問數(shù)組元素

可以通過下標(biāo)運(yùn)算符 [] 來訪問數(shù)組內(nèi)元素,數(shù)組的索引(即方括號中的值)從 0 開始。以一個包含 10 個元素的數(shù)組為例,它的索引為 0 到 9,而非 1 到 10。但在程序設(shè)計中,為了使用方便,我們通常會將數(shù)組開大一點(diǎn),不使用數(shù)組的第一個元素,從下標(biāo) 1 開始訪問數(shù)組元素。

例1

從標(biāo)準(zhǔn)輸入中讀取一個整數(shù) n n n,再讀取 n n n 個數(shù),存入數(shù)組中。其中, n ≤ 1000 n\leq 1000 n1000

#include <iostream>
using namespace std;

int arr[1001]; // 數(shù)組 arr 的下標(biāo)范圍是 [0, 1001)

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        cin >> arr[i];
    }
}

例2

(接例 1)求和數(shù)組 arr 中的元素,并輸出和。滿足數(shù)組中所有元素的和小于等于 2 31 ? 1 2^{31}-1 231?1

#include <iostream>
using namespace std;

int arr[1001];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        cin >> arr[i];
    }

    int sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        sum += arr[i];
    }

    printf("%d\n", sum);
    return 0;
}
越界訪問下標(biāo)

數(shù)組的下標(biāo) i d x \mathit{idx} idx 應(yīng)當(dāng)滿足 0 ≤ i d x < s i z e 0\leq \mathit{idx}< \mathit{size} 0idx<size,如果下標(biāo)越界,則會產(chǎn)生不可預(yù)料的后果,如段錯誤(Segmentation Fault),或者修改預(yù)期以外的變量。

多維數(shù)組

多維數(shù)組的實質(zhì)是「數(shù)組的數(shù)組」,即外層數(shù)組的元素是數(shù)組。一個二維數(shù)組需要兩個維度來定義:數(shù)組的長度和數(shù)組內(nèi)元素的長度。訪問二維數(shù)組時需要寫出兩個索引:

int arr[5][4];  // 一個長度為 5 的數(shù)組,它的元素是「元素為 int 的長度為的 4 的數(shù)組」
arr[2][3] = 1;  // 訪問二維數(shù)組

我們經(jīng)常使用嵌套的 for 循環(huán)來處理二維數(shù)組。

從標(biāo)準(zhǔn)輸入中讀取兩個數(shù) n n n m m m,分別表示黑白圖片的高與寬,滿足 n , m ≤ 1000 n,m\leq 1000 n,m1000。對于接下來的 n n n 行數(shù)據(jù),每行有用空格分隔開的 m m m 個數(shù),代表這一位置的亮度值。現(xiàn)在我們讀取這張圖片,并將其存入二維數(shù)組中。

const int maxn = 1001;
int pic[maxn][maxn];
int n, m;

cin >> n >> m;
for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
        cin >> pic[i][j];

類似地,你可以定義三維、四維,以及更高維的數(shù)組。

結(jié)構(gòu)體

結(jié)構(gòu)體(struct),可以看做是一系列稱為成員元素的組合體。

可以看做是自定義的數(shù)據(jù)類型。

本節(jié)描述的 struct 不同于 C 中 struct,在 C++ 中 struct 被擴(kuò)展為類似 class 的類說明符。

定義結(jié)構(gòu)體
struct Object
{
    int weight;
    int value;
} e[array_length];

const Object a;
Object b, B[array_length], tmp;
Object *c;

上例中定義了一個名為 Object 的結(jié)構(gòu)體,兩個成員元素 value、weight,類型都為 int。

} 后,定義了數(shù)據(jù)類型為 Object 的常量 a,變量 b,變量 tmp,數(shù)組 B,指針 c。對于某種已經(jīng)存在的類型,都可以使用這里的方法進(jìn)行定義常量、變量、指針、數(shù)組等。

訪問/修改成員元素

可以使用 變量名.成員元素名 進(jìn)行訪問。

如:輸出 varv 成員:cout << var.v。

也可以使用 指針名->成員元素名 或者 使用 (*指針名).成員元素名 進(jìn)行訪問。

如:將結(jié)構(gòu)體指針 ptr 指向的結(jié)構(gòu)體的成員元素 v 賦值為 tmp(*ptr).v = tmp 或者 ptr->v = tmp

結(jié)構(gòu)體的意義

首先,條條大路通羅馬,可以不使用結(jié)構(gòu)體達(dá)到相同的效果。結(jié)構(gòu)體的意義在于,它能夠顯式地將成員元素(在程序設(shè)計比賽中通常是變量)捆綁在一起,如本例中的 Object 結(jié)構(gòu)體,便將 value、weight 放在了一起(定義這個結(jié)構(gòu)體的實際意義是表示一件物品的重量與價值)。這樣的好處邊是限制了成員元素的使用。

想象一下,如果不使用結(jié)構(gòu)體而使用兩個數(shù)組 value[]、weight[],很容易寫混淆。但如果使用結(jié)構(gòu)體,能夠減輕出現(xiàn)使用變量錯誤的幾率。

并且不同的結(jié)構(gòu)體(結(jié)構(gòu)體類型,如 Object 這個結(jié)構(gòu)體)或者不同的結(jié)構(gòu)體變量(結(jié)構(gòu)體的實例,如上方的 e 數(shù)組)可以擁有相同名字的成員元素(如 tmp.value、b.value),同名的成員元素相互獨(dú)立(擁有獨(dú)自的內(nèi)存,比如說修改 tmp.value 不會影響 b.value 的值)。

這樣的好處是可以使用盡可能相同或者相近的變量去描述一個物品。比如說 Object 里有 value 這個成員變量;我們還可以定義一個 Car 結(jié)構(gòu)體,同時也擁有 value 這個成員;如果不使用結(jié)構(gòu)體,或許我們就需要定義 valueOfObject[],valueOfCar[] 等不同名稱的數(shù)組來區(qū)分。

如果想要更詳細(xì)的描述一種事物,還可以定義成員函數(shù)。C++ 中的類就是為此而生的。

結(jié)構(gòu)體的弊端

為了訪問內(nèi)存的效率更高,編譯器在處理結(jié)構(gòu)體中成員的實際存儲情況時,可能會將成員對齊在一定的字節(jié)位置,也就意味著結(jié)構(gòu)體中有空余的地方。因此,該結(jié)構(gòu)體所占用的空間可能大于其中所有成員所占空間的總和。

指針

變量的地址、指針

在程序中,我們的數(shù)據(jù)都有其存儲的地址。在程序每次的實際運(yùn)行過程中,變量在物理內(nèi)存中的存儲位置不盡相同。不過,我們?nèi)阅軌蛟诰幊虝r,通過一定的語句,來取得數(shù)據(jù)在內(nèi)存中的地址。

地址也是數(shù)據(jù)。存放地址所用的變量類型有一個特殊的名字,叫做「指針變量」,有時也簡稱做「指針」。

指針變量的大小與機(jī)器的位數(shù)有關(guān),32 位機(jī)器上的指針變量通常是 4 字節(jié),64 位機(jī)器上的指針變量通常是 8 字節(jié)。

地址只是一個刻度一般的數(shù)據(jù),為了針對不同類型的數(shù)據(jù),「指針變量」也有不同的類型,比如,可以有 int 類型的指針變量,其中存儲的地址(即指針變量存儲的數(shù)值)對應(yīng)一塊大小為 32 位的空間的起始地址;有 char 類型的指針變量,其中存儲的地址對應(yīng)一塊 8 位的空間的起始地址。

事實上,用戶也可以聲明指向指針變量的指針變量。

假如用戶自定義了一個結(jié)構(gòu)體:

struct TwoInt
{
    int a;
    int b;
};

TwoInt 類型的指針變量,對應(yīng)著一塊 2 × 32 = 64 bit 的空間。

指針的聲明與使用

C/C++ 中,指針變量的類型為類型名后加上一個星號 *。比如,int 類型的指針變量的類型名即為 int*

我們可以使用 & 符號取得一個變量的地址。

要想訪問指針變量地址所對應(yīng)的空間(又稱指針?biāo)?指向 的空間),需要對指針變量進(jìn)行 解引用(dereference),使用 * 符號。

int main()
{
    int a = 123; // a: 123
    int *pa = &a;
    *pa = 321; // a: 321
}

對結(jié)構(gòu)體變量也是類似。如果要訪問指針指向的結(jié)構(gòu)中的成員,需要先對指針進(jìn)行解引用,再使用 . 成員關(guān)系運(yùn)算符。不過,更推薦使用「箭頭」運(yùn)算符 -> 這一更簡便的寫法。

struct TwoInt
{
    int a;
    int b;
};

int main()
{
    TwoInt x{1, 2}, y{6, 7};
    TwoInt *px = &x;
    (*px) = y;   // x: {6,7}
    (*px).a = 4; // x: {4,7}
    px->b = 5;   // x: {4,5}
}
指針的偏移

指針變量也可以和整數(shù)進(jìn)行加減操作。對于 int 型指針,每加 1(遞增 1),其指向的地址偏移 32 位(即 4 個字節(jié));若加 2,則指向的地址偏移 2 × 32 = 64 位。同理,對于 char 型指針,每次遞增,其指向的地址偏移 8 位(即 1 個字節(jié))。

使用指針偏移訪問數(shù)組

我們前面說過,數(shù)組是一塊連續(xù)的存儲空間。而在 C/C++ 中,直接使用數(shù)組名,得到的是數(shù)組的起始地址。

int main()
{
    int a[3] = {0, 1, 2};
    int *p = a; // p 指向 a[0]
    *p = 4;     // a: [4, 1, 2]
    p = p + 1;  // p 指向 a[1]
    *p = 5;     // a: [4, 5, 2]
    p++;        // p 指向 a[2]
    *p = 6;     // a: [4, 5, 6]
}

當(dāng)通過指針訪問數(shù)組中的元素時,往往需要用到「指針的偏移」,換句話說,即通過一個基地址(數(shù)組起始的地址)加上偏移量來訪問。

我們常用 [] 運(yùn)算符來訪問數(shù)組中某一指定偏移量處的元素。比如 a[1] 或者 p[3]。這種寫法和對指針進(jìn)行運(yùn)算后再引用是等價的,即 p[5]*(p + 5) 是等價的兩種寫法。

空指針

在 C++11 之前,C++ 和 C 一樣使用 NULL 宏表示空指針常量,C++ 中 NULL 的實現(xiàn)一般如下:

// C++11 前
#define NULL 0

空指針和整數(shù) 0 的混用在 C++ 中會導(dǎo)致許多問題,比如:

// 函數(shù)重載
int f(int x);
int f(int* p);

在調(diào)用 f(NULL) 時,實際調(diào)用的函數(shù)的類型是 int(int) 而不是 int(int *)

為了解決這些問題,C++11 引入了 nullptr 關(guān)鍵字作為空指針常量。

C++ 規(guī)定 nullptr 可以隱式轉(zhuǎn)換為任何指針類型,這種轉(zhuǎn)換結(jié)果是該類型的空指針值。

nullptr 的類型為 std::nullptr_t,稱作空指針類型,可能的實現(xiàn)如下:

namespace std
{
    typedef decltype(nullptr) nullptr_t;
}

另外,C++11 起 NULL 宏的實現(xiàn)也被修改為了:

// C++11 起
#define NULL nullptr
指針的進(jìn)階使用

使用指針,使得程序編寫者可以操作程序運(yùn)行時中各處的數(shù)據(jù),而不必局限于作用域。

指針類型參數(shù)的使用

在 C/C++ 中,調(diào)用函數(shù)(過程)時使用的參數(shù),均以拷貝的形式傳入子過程中(引用除外,會在后續(xù)介紹)。默認(rèn)情況下,函數(shù)僅能通過返回值,將結(jié)果返回到調(diào)用處。但是,如果某個函數(shù)希望修改其外部的數(shù)據(jù),或者某個結(jié)構(gòu)體/類的數(shù)據(jù)量較為龐大、不宜進(jìn)行拷貝,這時,則可以通過向其傳入外部數(shù)據(jù)的地址,便得以在其中訪問甚至修改外部數(shù)據(jù)。

下面的 swap 方法,通過接收兩個 int 型的指針,在函數(shù)中使用中間變量,完成對兩個 int 型變量值的交換。

void swap(int *x, int *y)
{
    int t;
    t = *x;
    *x = *y;
    *y = t;
}

int main()
{
    int x = 5, y = 4;
    swap(&x, &y);
    // 調(diào)用后,main 函數(shù)中 x 變量的值變?yōu)?4,y 變量的值變?yōu)?5
}

C++ 中引入了引用的概念,相對于指針來說,更易用,也更安全。

動態(tài)實例化

除此之外,程序編寫時往往會涉及到動態(tài)內(nèi)存分配,即,程序會在運(yùn)行時,向操作系統(tǒng)動態(tài)地申請或歸還存放數(shù)據(jù)所需的內(nèi)存。當(dāng)程序通過調(diào)用操作系統(tǒng)接口申請內(nèi)存時,操作系統(tǒng)將返回程序所申請空間的地址。要使用這塊空間,我們需要將這塊空間的地址存儲在指針變量中。

在 C++ 中,我們使用 new 運(yùn)算符來獲取一塊內(nèi)存,使用 delete 運(yùn)算符釋放某指針?biāo)赶虻目臻g。

int* p = new int(100);
/* ... */
delete p;

上面的語句使用 new 運(yùn)算符向操作系統(tǒng)申請了一塊 int 大小的空間,將其中的值初始化為 100,并聲明了一個 int 型的指針 p 指向這塊空間。

同理,也可以使用 new 開辟新的對象:

class A
{
    int a;

public:
    A(int a_) : a(a_) {}
};

int main()
{
    A *p = new A(100);
    /* ... */
    delete p;
}

如上,「new 表達(dá)式」將嘗試開辟一塊對應(yīng)大小的空間,并嘗試在這塊空間上構(gòu)造這一對象,并返回這一空間的地址。

struct TwoInt
{
    int a;
    int b;
};

int main()
{
    TwoInt *p = new TwoInt{3, 2};
    /* ... */
    delete p;
}

{} 運(yùn)算符可以用來初始化沒有構(gòu)造函數(shù)的結(jié)構(gòu)。除此之外,使用 {} 運(yùn)算符可以使得變量的初始化形式變得統(tǒng)一,詳見 列表初始化 (C++11 起) - cppreference。

需要注意,當(dāng)使用 new 申請的內(nèi)存不再使用時,需要使用 delete 釋放這塊空間。不能對一塊內(nèi)存釋放兩次或以上。而對空指針 nullptr 使用 delete 操作是合法的。

動態(tài)創(chuàng)建數(shù)組

也可以使用 new[] 運(yùn)算符創(chuàng)建數(shù)組,這時 new[] 運(yùn)算符會返回數(shù)組的首地址,也就是數(shù)組第一個元素的地址,我們可以用對應(yīng)類型的指針存儲這個地址。釋放時,則需要使用 delete[] 運(yùn)算符。

int *p = new int[50];
delete[] p;

數(shù)組中元素的存儲是連續(xù)的,即 p + 1 指向的是 p 的后繼元素。

二維數(shù)組

在存放矩陣形式的數(shù)據(jù)時,可能會用到「二維數(shù)組」這樣的數(shù)據(jù)類型。從語義上來講,二維數(shù)組是一個數(shù)組的數(shù)組。而計算機(jī)內(nèi)存可以視作一個很長的一維數(shù)組。要在計算機(jī)內(nèi)存中存放一個二維數(shù)組,便有「連續(xù)」與否的說法。

所謂「連續(xù)」,即二維數(shù)組的任意一行(row)的末尾與下一行的起始,在物理地址上是毗鄰的,換言之,整個二維數(shù)組可以視作一個一維數(shù)組;反之,則二者在物理上不一定相鄰。

對于「連續(xù)」的二維數(shù)組,可以僅使用一個循環(huán),借由一個不斷遞增的指針即可遍歷數(shù)組中的所有數(shù)據(jù)。而對于非連續(xù)的二維數(shù)組,由于每一行不連續(xù),則需要先取得某一行首的地址,再訪問這一行中的元素。

這種按照「行(row)」存儲數(shù)據(jù)的方式,稱為行優(yōu)先存儲;相對的,也可以按照列(column)存儲數(shù)據(jù)。由于計算機(jī)內(nèi)存訪問的特性,一般來說,訪問連續(xù)的數(shù)據(jù)會得到更高的效率。因此,需要按照數(shù)據(jù)可能的使用方式,選擇「行優(yōu)先」或「列優(yōu)先」的存儲方式。

動態(tài)創(chuàng)建二維數(shù)組

在 C/C++ 中,我們可以使用類似下面這樣的語句聲明一個 N 行(row)M 列(column)5的二維數(shù)組,其空間在物理上是連續(xù)的。

int a[N][M];

這種聲明方式要求 N 和 M 為在編譯期即可確定的常量表達(dá)式。

在 C/C++ 中,數(shù)組的第一個元素下標(biāo)為 0,因此 a[r][c] 這樣的式子代表二維數(shù)組 a 中第 r + 1 行的第 c + 1 個元素,我們也稱這個元素的下標(biāo)為 (r,c)。

不過,實際使用中,(二維)數(shù)組的大小可能不是固定的,需要動態(tài)內(nèi)存分配。

常見的方式是聲明一個長度為 N × M 的 一維數(shù)組6,并通過下標(biāo) r * M + c 訪問二維數(shù)組中下標(biāo)為 (r, c) 的元素。

int* a = new int[N * M];

這種方法可以保證二維數(shù)組是 連續(xù)的。

此外,亦可以根據(jù)「數(shù)組的數(shù)組」這一概念來進(jìn)行內(nèi)存的獲取與使用。對于一個存放的若干數(shù)組的數(shù)組,實際上為一個存放的若干數(shù)組的首地址的數(shù)組,也就是一個存放若干指針變量的數(shù)組。

我們需要一個變量來存放這個「數(shù)組的數(shù)組」的首地址——也就是一個指針的地址。這個變量便是一個「指向指針的指針」,有時也稱作「二重指針」,如:

int** a = new int*[5];

接著,我們需要為每一個數(shù)組申請空間:

for (int i = 0; i < 5; i++)
{
    a[i] = new int[5];
}

至此,我們便完成了內(nèi)存的獲取。而對于這樣獲得的內(nèi)存的釋放,則需要進(jìn)行一個逆向的操作:即先釋放每一個數(shù)組,再釋放存儲這些數(shù)組首地址的數(shù)組,如:

for (int i = 0; i < 5; i++)
{
    delete[] a[i];
}
delete[] a;

需要注意,這樣獲得的二維數(shù)組,不能保證其空間是連續(xù)的。

還有一種方式,需要使用到「指向數(shù)組的指針」。

int main()
{
    int(*a)[5] = new int[5][5];
    int *p = a[2];
    a[2][1] = 1;
    delete[] a;
}

這種方式獲得到的也是連續(xù)的內(nèi)存,但是可以直接使用 a[n] 的形式獲得到數(shù)組的第 n + 1 行(row)的首地址,因此,使用 a[r][c] 的形式即可訪問到下標(biāo)為 (r, c) 的元素。

由于指向數(shù)組的指針也是一種確定的數(shù)據(jù)類型,因此除數(shù)組的第一維外,其他維度的長度均須為一個能在編譯器確定的常量。不然,編譯器將無法翻譯如 a[n] 這樣的表達(dá)式(a 為指向數(shù)組的指針)。

指向函數(shù)的指針

關(guān)于函數(shù)的介紹請參見本文 函數(shù) 章節(jié)。

簡單地說,要調(diào)用一個函數(shù),需要知曉該函數(shù)的參數(shù)類型、個數(shù)以及返回值類型,這些也統(tǒng)一稱作接口類型。

可以通過函數(shù)指針調(diào)用函數(shù)。有時候,若干個函數(shù)的接口類型是相同的,使用函數(shù)指針可以根據(jù)程序的運(yùn)行 動態(tài)地 選擇需要調(diào)用的函數(shù)。換句話說,可以在不修改一個函數(shù)的情況下,僅通過修改向其傳入的參數(shù)(函數(shù)指針),使得該函數(shù)的行為發(fā)生變化。

假設(shè)我們有若干針對 int 類型的二元運(yùn)算函數(shù),則函數(shù)的參數(shù)為 2 個 int,返回值亦為 int。下邊是一個使用了函數(shù)指針的例子:

#include <iostream>

int (*binary_int_op)(int, int);

int foo1(int a, int b) { return a * b + b; }

int foo2(int a, int b) { return (a + b) * b; }

int main()
{
    int choice;
    std::cin >> choice;
    if (choice == 1)
    {
        binary_int_op = foo1;
    }
    else
    {
        binary_int_op = foo2;
    }

    int m, n;
    std::cin >> m >> n;
    std::cout << binary_int_op(m, n);
}

可以使用 typdef 關(guān)鍵字聲明函數(shù)指針的類型。

typedef int (*p_bi_int_op)(int, int);

這樣我們就可以在之后使用 p_bi_int_op 這種類型,即指向「參數(shù)為 2 個 int,返回值亦為 int」的函數(shù)的指針。


函數(shù)

函數(shù)的聲明

編程中的函數(shù)(function)一般是若干語句的集合。我們也可以將其稱作「子過程(subroutine)」。在編程中,如果有一些重復(fù)的過程,我們可以將其提取出來,形成一個函數(shù)。函數(shù)可以接收若干值,這叫做函數(shù)的參數(shù)。函數(shù)也可以返回某個值,這叫做函數(shù)的返回值。

聲明一個函數(shù),我們需要返回值類型、函數(shù)的名稱,以及參數(shù)列表。

// 返回值類型 int
// 函數(shù)的名稱 function
// 參數(shù)列表 int, int
int function(int, int);

如上圖,我們聲明了一個名為 function 的函數(shù),它需要接收兩個 int 類型的參數(shù),返回值類型也為 int??梢哉J(rèn)為,這個函數(shù)將會對傳入的兩個整數(shù)進(jìn)行一些操作,并且返回一個同樣類型的結(jié)果。

實現(xiàn)函數(shù):編寫函數(shù)的定義

只有函數(shù)的聲明(declaration)還不夠,他只能讓我們在調(diào)用時能夠得知函數(shù)的 接口 類型(即接收什么數(shù)據(jù)、返回什么數(shù)據(jù)),但其缺乏具體的內(nèi)部實現(xiàn),也就是函數(shù)的 定義(definition)。我們可以在 聲明之后的其他地方 編寫代碼 實現(xiàn)(implement)這個函數(shù)(也可以在另外的文件中實現(xiàn),但是需要將分別編譯后的文件在鏈接時一并給出)。

如果函數(shù)有返回值,則需要通過 return 語句,將值返回給調(diào)用方。函數(shù)一旦執(zhí)行到 return 語句,則直接結(jié)束當(dāng)前函數(shù),不再執(zhí)行后續(xù)的語句。

int function(int, int); // 聲明

/* some other code here... */

int function(int x, int y)
{ // 定義
    int result = 5 * x + y;
    return result;
    result = 4; // 這條語句不會被執(zhí)行
}

在定義時,我們給函數(shù)的參數(shù)列表的變量起了名字。這樣,我們便可以在函數(shù)定義中使用這些變量了。

如果是同一個文件中,我們也可以直接將 聲明和定義合并在一起,換句話說,也就是在聲明時就完成定義。

int function(int x, int y) { return 5 * x + y; }

如果函數(shù)不需要有返回值,則將函數(shù)的返回值類型標(biāo)為 void;如果函數(shù)不需要參數(shù),則可以將參數(shù)列表置空。同樣,無返回值的函數(shù)執(zhí)行到 return; 語句也會結(jié)束執(zhí)行。

void say_happy()
{
    cout << "happy!\n";
    cout << "happy!\n";
    cout << "happy!\n";
    return;
    cout << "happy!\n"; // 這條語句不會被執(zhí)行
}

函數(shù)的調(diào)用

和變量一樣,函數(shù)需要先被聲明,才能使用。使用函數(shù)的行為,叫做「調(diào)用(call)」。我們可以在任何函數(shù)內(nèi)部調(diào)用其他函數(shù),包括這個函數(shù)自身。函數(shù)調(diào)用自身的行為,稱為 遞歸(recursion)。

在大多數(shù)語言中,調(diào)用函數(shù)的寫法,是 函數(shù)名稱加上一對括號 (),如 f()。如果函數(shù)需要參數(shù),則我們將其需要的參數(shù)按順序填寫在括號中,以逗號間隔,如 f(1, 2)。函數(shù)的調(diào)用也是一個表達(dá)式,函數(shù)的返回值 就是 表達(dá)式的值。

函數(shù)聲明時候?qū)懗龅膮?shù),可以理解為在函數(shù) 當(dāng)前次調(diào)用的內(nèi)部 可以使用的變量,這些變量的值由調(diào)用處傳入的值初始化??聪旅孢@個例子:

void f(int, int);

/* ... */

void f(int x, int y)
{
    x = x * 2;
    y = y + 3;
}

/* ... */

a = 1;
b = 1;
// 調(diào)用前:a = 1, b = 1
f(a, b); // 調(diào)用 f
         // 調(diào)用后:a = 1, b = 1

在上面的例子中,f(a, b) 是一次對 f 的調(diào)用。調(diào)用時,f 中的 xy 變量,分別由調(diào)用處 ab 的值初始化。因此,在 f 中對變量 xy 的修改,并不會影響到調(diào)用處的變量的值。

如果我們需要在函數(shù)(子過程)中修改變量的值,則需要采用「傳引用」的方式。

void f(int &x, int &y)
{
    x = x * 2;
    y = y + 3;
}

/* ... */

a = 2;
b = 2;
// 調(diào)用前:a = 2, b = 2
f(a, b); // 調(diào)用 f
         // 調(diào)用后:a = 4, b = 5

上述代碼中,我們看到函數(shù)參數(shù)列表中的「int」后面添加了一個「&(and 符號)」,這表示對于 int 類型的 引用(reference)。在調(diào)用 f 時,調(diào)用處 ab 變量分別初始化了 f 中兩個對 int 類型的引用 xy。在 f 中的 xy,可以理解為調(diào)用處 ab 變量的「別名」,即 f 中對 xy 的操作,就是對調(diào)用處 ab 的操作。

main 函數(shù)

特別的,每個 C/C++ 程序都需要有一個名為 main 的函數(shù)。任何程序都將從 main 函數(shù)開始運(yùn)行。

main 函數(shù)也可以有參數(shù),通過 main 函數(shù)的參數(shù),我們可以獲得外界傳給這個程序的指令(也就是「命令行參數(shù)」),以便做出不同的反應(yīng)。

下面是一段調(diào)用了函數(shù)(子過程)的代碼:

#include <iostream>

void say_happy()
{
    std::cout << "happy!\n";
    std::cout << "happy!\n";
    std::cout << "happy!\n";
}

int main()
{
    say_happy();
    say_happy();
}

文件操作

文件的概念

文件是根據(jù)特定的目的而收集在一起的有關(guān)數(shù)據(jù)的集合。C/C++ 把每一個文件都看成是一個有序的字節(jié)流,每個文件都是以 文件結(jié)束標(biāo)志(EOF)結(jié)束,如果要操作某個文件,程序應(yīng)該首先打開該文件,每當(dāng)一個文件被打開后(請記得關(guān)閉打開的文件),該文件就和一個流關(guān)聯(lián)起來,這里的流實際上是一個字節(jié)序列。

C/C++ 將文件分為文本文件和二進(jìn)制文件。文本文件就是簡單的文本文件(重點(diǎn)),另外二進(jìn)制文件就是特殊格式的文件或者可執(zhí)行代碼文件等。

文件的操作步驟

  1. 打開文件,將文件指針指向文件,決定打開文件類型;
  2. 對文件進(jìn)行讀、寫操作(比賽中主要用到的操作,其他一些操作暫時不寫);
  3. 在使用完文件后,關(guān)閉文件。

freopen 函數(shù)

函數(shù)簡介

函數(shù)用于將指定輸入輸出流以指定方式重定向到文件,包含于頭文件 stdio.h (cstdio) 中,該函數(shù)可以在不改變代碼原貌的情況下改變輸入輸出環(huán)境,但使用時應(yīng)當(dāng)保證流是可靠的。

函數(shù)主要有三種方式:讀、寫和附加。

函數(shù)原型
FILE* freopen(const char* filename, const char* mode, FILE* stream);
參數(shù)說明
  • filename: 要打開的文件名
  • mode: 文件打開的模式,表示文件訪問的權(quán)限
  • stream: 文件指針,通常使用標(biāo)準(zhǔn)文件流 (stdin / stdout) 或標(biāo)準(zhǔn)錯誤輸出流 (stderr)
  • 返回值:文件指針,指向被打開文件
文件打開格式
  • r:以只讀方式打開文件,文件必須存在,只允許讀入數(shù)據(jù) (常用)
  • r+:以讀/寫方式打開文件,文件必須存在,允許讀/寫數(shù)據(jù)
  • rb:以只讀方式打開二進(jìn)制文件,文件必須存在,只允許讀入數(shù)據(jù)
  • rb+:以讀/寫方式打開二進(jìn)制文件,文件必須存在,允許讀/寫數(shù)據(jù)
  • rt+:以讀/寫方式打開文本文件,允許讀/寫數(shù)據(jù)
  • w:以只寫方式打開文件,文件不存在會新建文件,否則清空內(nèi)容,只允許寫入數(shù)據(jù) (常用)
  • w+:以讀/寫方式打開文件,文件不存在將新建文件,否則清空內(nèi)容,允許讀/寫數(shù)據(jù)
  • wb:以只寫方式打開二進(jìn)制文件,文件不存在將會新建文件,否則清空內(nèi)容,只允許寫入數(shù)據(jù)
  • wb+:以讀/寫方式打開二進(jìn)制文件,文件不存在將新建文件,否則清空內(nèi)容,允許讀/寫數(shù)據(jù)
  • a:以只寫方式打開文件,文件不存在將新建文件,寫入數(shù)據(jù)將被附加在文件末尾(保留 EOF 符)
  • a+:以讀/寫方式打開文件,文件不存在將新建文件,寫入數(shù)據(jù)將被附加在文件末尾(不保留 EOF 符)
  • at+:以讀/寫方式打開文本文件,寫入數(shù)據(jù)將被附加在文件末尾
  • ab+:以讀/寫方式打開二進(jìn)制文件,寫入數(shù)據(jù)將被附加在文件末尾
使用方法

讀入文件內(nèi)容:

freopen("data.in", "r", stdin);
// data.in 就是讀取的文件名,要和可執(zhí)行文件放在同一目錄下

輸出文件內(nèi)容:

freopen("data.out", "w", stdout);
// data.out 就是輸出文件的文件名,和可執(zhí)行文件在同一目錄下

關(guān)閉標(biāo)準(zhǔn)輸入/輸出流:

fclose(stdin);
fclose(stdout);

printf / scanf / cin / cout 等函數(shù)默認(rèn)使用 stdin / stdout,將 stdin / stdout 重定向后,這些函數(shù)將輸入/輸出到被定向的文件。

模板
#include <cstdio>
#include <iostream>

int main(void)
{
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    /*
    中間的代碼不需要改變,直接使用 cin 和 cout 即可
    */
    fclose(stdin);
    fclose(stdout);
    return 0;
}

fopen 函數(shù)(選讀)

函數(shù)大致與 freopen 相同,函數(shù)將打開指定文件并返回打開文件的指針。程序設(shè)計中不常用到。

函數(shù)原型
FILE* fopen(const char* path, const char* mode)

各項參數(shù)含義同 freopen。

可用讀寫函數(shù)(基本)
  • fread / fwrite
  • fgetc / fputc
  • fscanf / fprintf
  • fgets / fputs
使用方式
FILE *in, *out;  // 定義文件指針
in = fopen("data.in", "r");
out = fopen("data.out", "w");
/*
do what you want to do
*/
fclose(stdin);
fclose(stdout);

C++ 的 ifstream / ofstream 文件輸入輸出流

使用方法

讀入文件內(nèi)容:

ifstream fin("data.in");
// data.in 就是讀取文件的相對位置或絕對位置

輸出到文件:

ofstream fout("data.out");
// data.out 就是輸出文件的相對位置或絕對位置

關(guān)閉標(biāo)準(zhǔn)輸入/輸出流

fin.close();
fout.close();
模板
#include <fstream>
using namespace std; // 兩個類型都在 std 命名空間里

ifstream fin("data.in");
ofstream fout("data.out");

int main(void)
{
    /*
    中間的代碼改變 cin 為 fin ,cout 為 fout 即可
    */
    fin.close();
    fout.close();
    return 0;
}

參考資料

  • OI Wiki: https://oi-wiki.org/
  • C++ Reference: https://zh.cppreference.com/

  1. 復(fù)合類型包括數(shù)組類型、引用類型、指針類型、類類型、函數(shù)類型等。由于本文是面向初學(xué)者的,故不在本文做具體介紹。具體請參閱 類型 - cppreference.com ??

  2. 詳見 值類別 - cppreference。 ??

  3. 不包含寬字符類型、位域和枚舉類型,詳見 整型轉(zhuǎn)換 - cppreference。 ??

  4. 定義一個變量時,除了類型說明符之外,還可以包含其他說明符。詳見 聲明 - cppreference。 ??

  5. 更通用的方式是使用第 n 維(dimension)的說法。對于「行優(yōu)先」的存儲形式,數(shù)組的第一維長度為 N,第二維長度為 M。 ??

  6. 實際上,數(shù)據(jù)在內(nèi)存中都可以視作線性存放的,因此在一定的規(guī)則下,通過動態(tài)開辟一維數(shù)組的空間,即可在其上存儲 n 維的數(shù)組。 ??文章來源地址http://www.zghlxwxcb.cn/news/detail-515017.html

到了這里,關(guān)于C++ 程序設(shè)計入門的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Python 程序設(shè)計入門(017)—— 選擇結(jié)構(gòu)程序設(shè)計

    程序中的選擇結(jié)構(gòu)也稱為判斷結(jié)構(gòu),按照條件選擇執(zhí)行不同的代碼片段。Python 中的選擇結(jié)構(gòu)主要有三種形式:if 語句、if…else 語句和 if…elif…else 語句。 if 語句的語法格式如下: 說明: (1)表達(dá)式:可以是比較表達(dá)式或邏輯表達(dá)式。如果表達(dá)式的值為 True,則執(zhí)行語句塊

    2024年02月13日
    瀏覽(84)
  • C++《面向?qū)ο蟪绦蛟O(shè)計課程設(shè)計》

    《面向?qū)ο蟪绦蛟O(shè)計課程設(shè)計》課程說明 適用專業(yè):計算機(jī)科學(xué)與技術(shù) 課程周數(shù):5周 一、根據(jù)計算機(jī)科學(xué)與技術(shù)專業(yè)人才培養(yǎng)方案制訂。 (一)課程設(shè)計性質(zhì) 課程設(shè)計是學(xué)生對課程所學(xué)知識的綜合運(yùn)用,它與課堂聽講、上機(jī)實驗、課外練習(xí)、自學(xué)研究相輔相成,構(gòu)成一個

    2024年02月08日
    瀏覽(23)
  • 【高級程序設(shè)計語言C++】特殊類設(shè)計

    拷貝只會放生在兩個場景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此想要讓一個類禁止拷貝,只需讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載即可。 用C++11的話,可以使用特殊的語法來實現(xiàn)一個不能被拷貝的類。在C++11中,可以使用刪除函數(shù)(deleted function)來禁用拷

    2024年02月10日
    瀏覽(23)
  • Python 程序設(shè)計入門(022)—— 循環(huán)結(jié)構(gòu)程序設(shè)計(3):循環(huán)的嵌套

    在編程時,循環(huán)語句的嵌套是很常見的,循環(huán)嵌套是指在一個循環(huán)語句中又包含另一個循環(huán)語句。例如:在 for 循環(huán)中嵌入另一個 for 循環(huán)或 while 循環(huán),在 while 循環(huán)中嵌入另一個 while 循環(huán)或 for 循環(huán)等。 1、使用雙層 for 循環(huán) 代碼如下: 代碼如下: 1、使用 for 循環(huán)中嵌套 w

    2024年02月12日
    瀏覽(19)
  • 【C++ 程序設(shè)計】第 1 章:C++ 語言簡介

    【C++ 程序設(shè)計】第 1 章:C++ 語言簡介

    目錄 一、C++ 語言的發(fā)展簡史 二、C++ 語言的特點(diǎn) (1)基本的輸入/輸出 (2)頭文件和命名空間 (3)強(qiáng)制類型轉(zhuǎn)換運(yùn)算符? (4)函數(shù)參數(shù)的默認(rèn)值? (5)引用和函數(shù)參數(shù)的傳遞 ①?引用的定義 ②?引用在函數(shù)中的使用 (6)const 與指針共同使用 (7)內(nèi)聯(lián)函數(shù)? (8)函數(shù)的

    2024年02月07日
    瀏覽(19)
  • 【高級程序設(shè)計語言C++】初識模板

    【高級程序設(shè)計語言C++】初識模板

    概念: 函數(shù)模板代表了一個函數(shù)家族,該函數(shù)模板與類型無關(guān),在使用時被參數(shù)化,根據(jù)實參類型產(chǎn)生函數(shù)的特定類型版本。 具體格式: templatetypename T1, typename T2,…,typename Tn 返回值類型 函數(shù)名(參數(shù)列表){} 輸出結(jié)果: typename是用來定義模板參數(shù),也可以使用class(切記

    2024年02月15日
    瀏覽(19)
  • C++程序設(shè)計函數(shù)部分(定義+實例)

    C++程序設(shè)計函數(shù)部分(定義+實例)

    目錄 1、內(nèi)聯(lián)函數(shù) 2、默認(rèn)形參值函數(shù) 3、重載函數(shù) 4、系統(tǒng)函數(shù) (1)定義 在函數(shù)前面加上 inline 申明 eg: inline double CalArea(double radius) { return 3.14*radius*radius; } void main() { double r(3.0); double area; area=CalArea(r); coutareaendl; } (2)作用 提高運(yùn)行的速度。 對于一些程序代碼小,運(yùn)行時間

    2023年04月14日
    瀏覽(18)
  • 【C++ 程序設(shè)計】實戰(zhàn):C++ 實踐練習(xí)題(1~10)

    【C++ 程序設(shè)計】實戰(zhàn):C++ 實踐練習(xí)題(1~10)

    目錄 01. 二維數(shù)組反對角線之和 02. 奇偶性? 03. 指針與變量 04. 員工薪資? 05. 整型值(%4d?進(jìn)行格式化) 06. 求三個數(shù)中的最大值和最小值 07. 同一字母次數(shù)統(tǒng)計 08. 字符串回文判斷 09. 閏年判斷 10.?交換兩個雙精度數(shù) 【代碼詳解】 以上代碼 計算的是 反對角線(從右上角到左下

    2024年02月14日
    瀏覽(31)
  • C++程序設(shè)計基礎(chǔ)【五】(引用和指針)

    引用是對象的替代名稱,以簡化實體之間的通信。當(dāng)聲明一個引用變量時,不會在內(nèi)存中創(chuàng)建一個新的對象,而只是聲明一個現(xiàn)有變量的替代名稱 引用變量在原始變量前加上r并將首字母大寫,形成約定,方便查看綁定的對象 1.復(fù)合類型 引用類型是復(fù)合類型,但不能用double的

    2024年02月07日
    瀏覽(23)
  • Java程序設(shè)計復(fù)習(xí)提綱(上:入門語法)

    Java程序設(shè)計復(fù)習(xí)提綱(上:入門語法)

    上:本文 基本語法與編譯運(yùn)行 數(shù)據(jù)類型和 常用語法 數(shù)組與字符串 異常處理 中: Java程序設(shè)計復(fù)習(xí)提綱(中:面向?qū)ο螅?- 孤飛 - 博客園 (cnblogs.com) 面向?qū)ο蠛皖?下: Java程序設(shè)計復(fù)習(xí)提綱(下:圖形界面) - 孤飛 - 博客園 (cnblogs.com) 圖形界面 java沒有指針沒有全局變

    2024年02月05日
    瀏覽(44)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包