作者:李佳樺
日期:2021/04/16

基礎架構

// 使用名稱為iostream的標頭檔,用於資料輸入輸出 (應該吧?
#include <iostream> 
using namespace std; // 定義名稱為 std 的命名空間 
int main(){ // 主程式,程式由此開始執行
    cout << "Hello World!" << endl; // 輸出指令
    return 0; // 程式結束,可省略
}

標頭檔 Header files

標頭檔能夠引入函式庫中,如 camth, cctype …
C++ 使用了 #include 這個指令來載入標頭檔

//萬用標頭檔
#include <bits/stdc++.h> 

註解 comment

在程式碼中做適當的註解能使其更容易閱讀

// 這是單行註解

/*
    這是多行註解
    可以換行
*/

縮排 Indent

我們可以用 TAB 鍵來進行縮排
增加程式碼的可讀性
如果只有單行敘述句則可以省略大括號

// 未縮排
while(1){cout << 12 << endl;}
// 縮排後
while(1){
    cout << 12 << endl;
}
// 省略
while(1)
    cout << 12 << endl;

敘述句

敘述句以分號 ; 隔開

int a;
a=5;
cout << a << endl;

區塊 Block

C++用大括號 { } 來表示一個區塊
通常加在函式、條件句後

// while
while(1){
    cout << "Hello";
}

// if
if(a>0){
    cout << "Hey";
}
    
// 建立函式
int func(int a, int b){
    return a+b;
}

輸入輸出 input & output

在C++中,我們使用 cin 和 cout 以在終端機獲取和輸出資料
<< 和 >> 代表資料流方向

int a; // 宣告變數 a
cin >> a; //輸入 a
cout << a+1; //輸出a+1
// input: 5  
// output: 6

我們還能使用多個 << 或 >> 來輸入輸出多個資料

int a, b; // 宣告變數 a b
cin >> a >> b; 

cout << "總合為: " << a+b << endl; // 輸出 a+b
// input: 2 3
// output: 總合為: 5

習題

zerojudge d483
CJOJ 1003

命名規則

C++的變數名稱遵守著一定的規則
當名稱和關鍵字(keyword)重複或不合法時,會造成無法編譯的錯誤

  • 變數名稱的第一個字不可為數字( 0~9 )或符號( @ , & , # … )但可為底線( _ )
  • 變數名稱不可使用中文
  • 英文字母的大小寫不同,代表的東西也不同,因此我們可以利用此特性來避開關鍵字的使用

常數 CONSTANT

常數在程式中為固定不變的數,無法更改其初始值
我們可以使用 #define 和 const 來定義或宣告一個常數

#define Pi 3.14159
// #define 可以用來定義一個字詞,當那個詞被編譯時,會被轉換成所定義的程式碼
// Pi 常數名稱 
// 3.14159 常數初始值

const double Pi 3.14159
// const 宣告這個數為常數
// double 常數型態,不可省略

資料型態 DATA TYPE

資料型態中文意義位元組(byte)範圍
short短整數2-32768 ~ 32767
int整數4-2,147,483,648 ~ 2,147,483,647
long long int長整數89.22×1018 9.22×1018-9.22\times 10^{18}~9.22\times 10^{18}
char字元40 ~ 255
bool布林值1false 或 true (0 或 1)
float浮點數41.17×1038 3.4×10381.17\times 10^{-38}~3.4\times 10^{38}
double倍浮點數82.25×10308 1.79×103082.25\times 10^{-308}~1.79\times 10^{308}

浮點數的有效位數至小數點後第7位,而倍浮點數的有效位數至小數點後第15位

有號無號 SIGNED & UNSIGNED

型態可以分成 有號 ( signed ) 和 無號 ( unsigned ) 兩種
預設值為有號

short a; // 有號整數 -32768 ~ 32767
unsigned short b //無號整數 0 ~ 65535

溢位 OVERFLOW

如果指定給變數的值超過變數型態的範圍,將會發生溢位的錯誤

編譯器不會對可能會溢位的程式碼發出警告

short a = 32768; // 範圍: -32768 ~ 32767
cout << a << endl; // output: -32768

跳脫字元 ESCAPE CHARACTER

在反斜線 \ 後加上字元
用於對電腦的命令或表示不可列印的字元

跳脫字元結果
\a讓蜂鳴器發出嗶聲 ( alarm )
\b退後一格 ( backspace )
\f換頁 ( form feed )
\n換行 ( line feed )
\r歸位 ( carriage return )
\t水平制表 ( horizontal tab )
\v垂直製表 ( vertical tab )
\‘顯示單引號 ’
\“顯示雙引號 “
\ \顯示反斜線 \

資料型態轉換

double a = 1.23;
int b = a; // 1.23 -->  1 (無條件捨去)
cout << (int)a; //強制轉換型態 浮點數 --> 整數

int p = 2;
double q = 1.2;
cout << p+q; // 3.2
// 先把 p 轉換成 double,再與 q 相加

大型態轉換至小型態時將會失去原本的精度
編譯器進行不同型態的運算時會先把精度小的數字轉換成精度大的

習題

CJOJ 1002

運算子

在程式中,我們能使用運算元和運算子來進行算數運算、邏輯判斷…等
其中運算元可以是常數、變數、數字或是有回傳值的函式
我們將運算元和運算子的組合稱為運算式

算數運算子

運算子功能用法
+a + b
-a - b
*a * b
/a / b
%取餘數a % b
++遞增a++
遞減b–
int a=13;
int b=5;
cout << a + b; // 18
cout << a - b; // 8
cout << a * b; // 65
cout << a / b; // 2
cout << a % b; // 3

遞增和遞減根據位置的不同而有不同的結果

int a=2;
cout << a++; // 2
cout << a; // 3

int b=2;
cout << ++b; //3
cout << b; //3

當 ++ 或 – 在前面時,本身會先加 1 後再進行運算
而當 ++ 或 – 在後面時,進行運算後本身才加上 1

複合指定運算子

我們用等號 (指定運算子) = 來指定變數的值
而 指定運算子 搭配 算術運算子 後形成 複合指定運算子

運算子用法
+=a+=b
-=a-=b
*=a*=b
/=a/=b
%=a%=b
int a=5;
a+=3; // a=a+3 將 a 的值加上 3 之後再指定給 a  ∴ a = 5 + 3 = 8
a-=2; // a=a-2 同理,將 a 的值減去 2 之後再指定給 a  ∴ a = 8 - 2 = 6
a*=4; // a=a*4  ∴ a = 6 * 4 =24
a/=3; // a=a/3 ∴ a = 24 / 3 = 8
a%=7; // a=a%7 ∴ a = 8 % 7 = 1

關係運算子

我們能用下列運算子來進行條件的判斷:

運算子功能用法
>大於a>b
<小於a<b
==等於a==b
>=大於等於a>=b
<=小於等於a<=b
!=不等於a!=b
int a=3, b=5, c=7;
cout << (a>b); // false
cout << (b==5); // true
cout << (c>=7); // true
cout << (a!=2); // true

邏輯運算子

我們能使用邏輯運算子能進行邏輯的判斷

運算子功能用法
&&且 (交集)A && B
||或 (聯集)A || B
!不等於!A
int a=3, b=5, c=7;
cout << (a>2 && c<6); // false
cout << (b==5 || a>4) // true
cout << !(a>=3 && c<=8) // false

A && B ,當A和B皆為 True 時,結果為 True,其餘皆為 False
A || B ,當A和B皆為 False 時,結果為 False,其餘皆為 True

條件運算子

條件運算子 又稱為 三元運算子,其傳回值依條件式的結果而定
如果條件式的結果為 True,則回傳冒號前的值,若結果為 False,則回傳冒號 : 後的值

int a=5, b=9;
cout << (a>b ? "yes" : "no"); // no

條件判斷與迴圈

條件判斷

IF

當 if 的判斷條件為 True 時,將會執行 if 區塊內的敘述
若結果為 False ,則跳過此區塊

int a=7, b=9;
if(a>b){ // False
    cout << "Hello" << endl; // 跳過 if 區塊內的敘述
}
if(b>5){ // True
    a+=b; // a=a+b ∴a=16
}

IF … ELSE

當 if 的判斷條件為 True 時,將會執行 if 區塊內的敘述
若結果為 False ,則執行 else 區塊內的敘述

int a=5, b=7;
if(a>b){ // False
    cout << "OK" << endl;
    // 跳過 if 區塊內的敘述
}
else{ // 當 if 條件結果為 False
    cout << "NO" << endl; // NO
    // 執行 else 區塊內的敘述
}

ELSE IF

else if 可以放在 if 和 else 中間,用以判斷其他條件

int a=5, b=7;
if(a>b){ // False
    cout << "Perfect" << endl;
    // 跳過此區塊內的敘述,進入 else if 的區塊 
}
else if(a<4){ // False
    cout << "fabulous" << endl;
    // 跳過此區塊內的敘述 
}
else if(b>5){ // True
    cout << "Nice" << endl; // Nice
    // 當上面區塊的條件結果為 False 且自己的條件結果為 True
    // 跳出流程判斷
}
else{
    cout << "Good" << endl;
    // 不會執行到此區塊
}

else if 的數量可以根據自己所需增加數量

SWITCH

switch 可以用來判斷單一條件
在條件式數量多的情況下,能使程式碼變得相對簡潔

int a=10;
switch(a/3){ // a/3 是switch的條件句
    // 如果 a/3 和 case 後的數字或字元相等,則執行此case區塊的敘述
    case 2: // a/3 != 2   False
        cout << "Red" << endl;
        break;
    case 3: // a/3 == 3 結果為 True ,進入敘述
        cout << "Green" << endl; // Green
        break; // 跳出 switch 的判斷流程
    case 4: // 此 case 區塊不會被執行
        cout << "Blue" << endl;
        break;
    default: // 若上面所有 case 皆不符合,則執行 default 區塊的敘述
        cout << "Color" << endl;
        break;
}

case 後面必須加上冒號,而不是大括號
case 的敘述結束後,需要加上 break ,跳出 switch,否則會繼續向下執行另一個 case

迴圈

FOR

for 迴圈必須事先指定變數的初始值、結束條件及每執行一次變數的增減值

for(int i=0;i<10;i++){
    // Code
}

int i=0 : 指定變數初始值
i<10 : 結束條件,當 i 等於 10 時,跳出迴圈
i++ : 控制變數增減,每跑一次迴圈就執行一次

for(int i=0;i<5;i++){
    cout << "i=" << i << endl;
}
/* 
    當 i=0 ,判斷 i<5 為 True ,輸出 i=0 , i 加 1
    當 i=1 ,判斷 i<5 為 True ,輸出 i=1 , i 加 1
    當 i=2 ,判斷 i<5 為 True ,輸出 i=2 , i 加 1
    當 i=3 ,判斷 i<5 為 True ,輸出 i=3 , i 加 1
    當 i=4 ,判斷 i<5 為 True ,輸出 i=4 , i 加 1
    當 i=5 ,判斷 i<5 為 False ,跳出迴圈
*/

WHILE

當 while 迴圈的條件句為 True 時,便會重複執行下去
我們可以在 while 迴圈中加入變數,進行迴圈次數的控制

int a=5;
while(a>0){ // while 的條件句, 當結果為 False, 跳出迴圈
    cout << "a=" << a << endl;
    a--;
}
/*
    當 a=5 ,判斷 a>0 為 True,輸出 a=5 ,a減1 
    當 a=4 ,判斷 a>0 為 True,輸出 a=4 ,a減1
    當 a=3 ,判斷 a>0 為 True,輸出 a=3 ,a減1
    當 a=2 ,判斷 a>0 為 True,輸出 a=2 ,a減1
    當 a=1 ,判斷 a>0 為 True,輸出 a=1 ,a減1 
    當 a=0 ,判斷 a>0 為 False,跳出迴圈
*/

DO … WHILE

我們可以使用 do … while 來使我們的程式碼至少被執行一次

int i=1
do{
    // 進來迴圈時,先不進行條件判斷
    cout << "Only one" << endl; 
    i--;
}while(i>0); // 如果條件句為 True ,再次進入迴圈,否則跳出迴圈

迴圈控制指令

我們可以藉由迴圈控制指令,使的迴圈直接進入下一次的迴圈或結束
常見的指令有 break 、 continue

CONTINUE

for(int i=1;i<11;i++){ // 從 i=1 執行到 i=10
    if(i%2==0) //如果i整除2
        continue; // 進入下一次迴圈
    // 只有當 i 不被 2 整除才會被輸出
    cout << i << " "; // 1 3 5 7 9
}

BREAK

for(int i=1;i<11;i++){ // 從 i=1 執行到 i=10
    if(i%2==0) //如果i整除2
        continue; // 進入下一次迴圈
    // 只有當 i 不被 2 整除才會被輸出
    cout << i << " "; // 1 3 5 7 9
    
}

習題

CJOJ 1001
CJOJ 1004
CJOJ 1005
CJOJ 1007
CJOJ 1008
CJOJ 1021

陣列

一維陣列

陣列是由許多相同型態的變數所串連而成的
我們可以將陣列想成各自有不同編號的收納盒

第一格的編號為 0 ,第二格的編號為 1 … 以此類推

// 宣告陣列
int arr[3]={4,5,6}
// 資料型態 陣列名稱[陣列大小]={初始值0,初始值1,...}

// 取得存在陣列中的值
arr[1] // 5
// 陣列名稱[索引值]
  1. 陣列的大小是固定不變的
  2. 如果陣列的大小超過記憶體的大小,將會發生錯誤
    這時可以試著在 main 的區塊外宣告陣列
  3. 宣告陣列時不一定要給予初始值
    不過我們可以將陣列中所有值初始化為 0
int array[10] = { 0 }

二維陣列

我們可以把二維陣列想像成一個平面的收納盒

int array2D[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
cout << array2D[1][0] + array2D[2][2] + array2D[0][2] << endl; // 16
/*
    1 2 3
    4 5 6
    7 8 9
    
    array2D[1][0] → 第 2 列 第 1 行 → 4
    array2D[2][2] → 第 3 列 第 3 行 → 9
    array2D[0][2] → 第 1 列 第 3 行 → 3
*/

訪問陣列

我們能利用迴圈來訪問陣列

int array[3] = {1, 2, 3}; // 宣告陣列
for(int i=0;i<3;i++){ // 從 i=0 到 i=2
    cout << array[i] << endl; //輸出索引值為 i 的值
}

int inputArray[5]; // 宣告一個空陣列
for(int i=0;i<5;i++){ // 從 i=0 到 i=5
    cin >> inputArray[i]; // 讓使用者輸入陣列的值
}

int array2d[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
for(int i=0;i<3;i++){ // 從 i=0 到 i=2
    // 記得相同區塊內不能有名稱相同的變數
    for(int j=0;j<3;j++){ // 從 j=0 到 j=2
        array2d[i][j]+=2; // 全部加2
        // { {3,4,5},{6,7,8},{9,10,11} }
    }
}
/* 
    訪問順序: 
    array2d[0][0] => array2d[0][1] => array2d[0][2] =>
    array2d[1][0] => array2d[1][1] => array2d[1][2] =>
    array2d[2][0] => array2d[2][1] => array2d[2][2]
*/

氣泡排序法 - 概念

從陣列的開頭不斷的比較兩個數字
如果前面的數字比較大,則兩者交換,反之則不交換
第一次要檢查陣列長度-1次,隨後每次檢查的次數會減1,直到完成排序

C++的函式庫中有內建排序函數 sort():

int arr[3] = {2,3,1}
sort(arr, arr+3); // {1,2,3}

練習題

CJOJ 1019
zerojudge c067