C++[0]

C++ 小社課第一堂

課程目錄

課程目錄

甚麼是C++?

程式語言介紹

C++

是一種被廣泛使用的中階電腦程式設計語言。

組合語言

C++

Python

接近機器語言

可直接操作硬體與

記憶體

對機器抽象程度高

接近人類語言

介於兩者之間

競程方面

C++的效率高(較python而言)

有強大的標準模板庫(STL)

競程的線上題解、模板幾乎都以C++為主

......

總之C++適合競程

缺點/危險性

寫C++時,大部分時間會需要手動管理記憶體

然後有時候就會發生:

Memory leak

Dangling pointer

Buffer overflow(哈哈你迴圈是不是寫爛了)

Use-after-free

等各種導致記憶體炸掉的fatal error

還有其他例子

像 srcatch 當機只會是網頁,或者 App 本身

而 C++ 是可以干涉硬體運作的

怎麼寫C++?

編輯器、編譯器

編輯器與編譯器

你在打扣的時候,是在編輯器上打扣。

編譯器負責將你打的扣轉成電腦看得懂的編碼。

還會進行優化跟報錯

編輯器的選擇

VS code

Vim

Emacs

編輯器的介面

編輯器會將對你打的扣上色

讓你的扣會比較好讀

 

然後這份扣肉眼可見的難讀

不要學我都不空格

也不要main(100)

阿今天用甚麼

因為VS code、Vim這些不是安裝需要時間、就是門檻高

痾絕對不是因為我不會

所以今天用一個線上編輯器:online gdb

介面介紹

執行程式

除錯模式

強制中止程式

記得選語言(C++)才能編譯

介面介紹

執行程式後的介面(console)

顯示輸出

C++ 初始模板講解

要開始報錯囉

/******************************************************************************

Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, OCaml, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <iostream>

int main()
{
    std::cout<<"Hello World";

    return 0;
}

這是你在把語言調成C++後出現的扣

上面的/**...是註解,不影響程式,先把它砍了

#include <iostream>

int main()
{
    std::cout << "Hello World";

    return 0;
}

你會發現,這份扣跟講義上的有點區別,講義有寫原因

請先將扣改成講義上的格式

記得大括號不要換行

#include <iostream>
using namespace std;

int main() {
    cout << "Hello World";
    return 0;
}

跑跑看

會出現這個console,顯示你的輸出

小小小練習

請將編輯器中的扣全部刪掉,嘗試自己寫出一個完整的初始模板

過程中遇到報錯時可參考講義對應的解決方法

輸入輸出

大於小於不要寫反

cin 與 cout

cin >> a >> b;
cout << a << b;

cin >> 的箭頭是向右,將輸入丟進變數裡

cout << 的箭頭是向左,將變數丟到輸出裡

輸入是在 console 裡輸入

變數怎麼做?

int

int a, b;

int 是整數

宣告兩個變數,名稱為 a 與 b

順帶一提,如果宣告時沒有給定一個固定值的話,a 與 b 的值可能會亂跑,所以記得歸零

int a = 0, b = 0;

這裡講的變數其實就是 srcatch 的變數

只是能存的資料類型有限,下一單元講

endl

cout << a << endl;

endl : end of line

 

用處是換行

 

記得要加,不然之後使用 vim 之類的編輯器會出事

小小小練習

int a, b;

輸入 a 與 b 兩個整數,輸出 b 與 a 兩個整數,兩個數字中間要換行

輸入與輸出的數字順序相反

範例:

輸入 1 3

輸出

3

1

阿記得加分號

解答

int a, b;
cin >> a >> b;
cout << b << endl;
cout << a << endl;

有記得加分號嗎

運算子(數學)

+-*/=

還有%

int a;
a = 1 + 2;
cout << a << endl; //3
int a;
a = 1 * 2;
cout << a << endl; //2
int a;
a = 1 - 2;
cout << a << endl; //-1

加法

 

乘法

int a;
a = 3 / 2;
cout << a << endl; //1
int a;
a = 5 % 3;
cout << a << endl; //2

除法(取商)

取模(取餘數)

為甚麼 / 是取商?

下個單元會講

int a, b;
b = 3;
a = b;
cout << a << endl //3

=(賦值)

將 = 右邊的值丟到左邊的變數裡

不要記反

喔對後面還會講 ==

不要打錯

小小小實作

輸入兩個數字 a,b

輸出 a+b 的值

cin >> a >> b;
cout << a + b << endl;

資料型態

很多

各種資料型態

名稱 型態
int 整數 
long int 整數
long long int 整數
unsigned long long 非負整數
bool
float 小數                          精度6~7位
double 小數                          精度15~16位
long double 小數                          精度18~21位
char 字元
string 字串(多個字元)
{-2^{31}} \sim {2^{31}-1}
{-2^{31}} \sim {2^{31}-1}~or~{-2^{63}} \sim {2^{63}-1}
{-2^{63}} \sim {2^{63}-1}
{0} \sim {2^{64}-1}
0~or~1
\pm {3.4e{38}}
\pm {1.797e{308}}
\pm {1.797e{308}}

long long int 非常重要

有時候競程會遇到這種題目

a,b < {1e15}

(輸入)

這時候,用 int 與 long long int 會有不同的結果

int a;
long long int b;
a = 1e15;
cout << a << endl;
b = 1e15;
cout << b << endl; 

所以記得有些題目要開 long long int

例如暑訓小比賽

為何 int 做除法時只會得到商

int 做計算時,得出的答案型態也是 int

而 int 只能存整數,不能存小數

對於小數會進行向下取整

因此 3/2 再向下取整 -> 1

int a;
a = 3 / 2;
cout << a << endl; //1

那這樣呢?

3 與 2 都是 int,int 運算得出的數值也是 int

即使 a 是浮點數,但 3/2的結果是 1

再用 = 賦值,a = 1

float a;
a = 3 / 2;
cout << a << endl; //???

那這樣呢?

2.0 3.0 是浮點數,計算中有浮點數時,

得出的結果就是浮點數

所以三種狀況皆為 a = 1.5

float a;
a = 3 / 2.0;
cout << a << endl; //???
a = 3.0 / 2
cout << a << endl; //???
a = 3.0 / 2.0
cout << a << endl; //???

精度是甚麼意思

4/3=1.333333......

那他會真的一直都是 3 嗎?

float a;
a = 4 / 3.0;
cout << a << endl; //1.33333
printf("%.7f\n",a); //輸出7位小數
printf("%.10f\n",a); //輸出10位小數
printf("%.15f\n",a); //輸出15位小數

float 的精度只有 7 位

也就是說小數超過 7 位的部分,float 會爛掉

而 double 與 long double則不會

精度是甚麼意思

double 與 long double 的精度較高

數值較為準確

在某些需要精確計算的時候很重要

double a;
a = 4 / 3.0;
cout << a << endl; //1.33333
printf("%.7f\n",a); //輸出7位小數
printf("%.10f\n",a); //輸出10位小數
printf("%.15f\n",a); //輸出15位小數
long double a;
a = 4 / 3.0;
cout << a << endl; //1.33333
printf("%.7f\n",a); //輸出7位小數
printf("%.10f\n",a); //輸出10位小數
printf("%.15f\n",a); //輸出15位小數

運算子(邏輯)與邏輯判斷

記得前面講的 == 嗎

bool

bool 中存的 0 與 1,其實就是 false and true

bool a = true;
cout << a << endl; //1

if

bool a = true; //也可以寫false
if(a) {
	cout << "Hello World!" << endl;
}
else {
	cout << "Nah this script won't run to here" << endl;
}

這是判斷條件

如果條件成立,則執行此程式

如果條件不成立,則執行此程式

a 為甚麼可以放在這裡?

由於 C++ 中的條件判斷,也是回傳 true or false

所以 bool 放在這裡是沒有問題的

不如說這裡本來就是放 bool 的資料型態

而 a 為 true,所以此條件永遠成立

邏輯運算子

== 當兩邊的值相等,回傳 true,反之回傳 false

!= 兩邊的值不相等,回傳 true,反之回傳 false

&& 當左右兩個條件皆為 true,回傳 true,反之回傳 false

|| 當左右兩個條件至少其中一個為 true,回傳 true,反之回傳 false

! 當條件為 true,回傳 false,反之回傳 true

範例

這是一個判斷奇偶的程式

int a;
cin >> a;
if (a % 2 == 1) {
	cout << "odd" << endl;
} 
else {
	cout << "even" << endl;
}

|| 與 && 的用法

這是一個判斷閏年的程式

如果 a 是 400 的倍數:閏年

否則如果 a 是 4 的倍數, a 不是 100 的倍數:閏年

......

int a;
cin >> a;
if (a % 400 == 0) {
	cout << "leap year" << endl;
}
else if(a % 4 == 0 && a % 100 != 0) { //else if講義有細說
	cout << "leap year" << endl;
}
else {
	cout << "normal year" << endl;
}

組合條件的用法

如果 a 是 2 的倍數, a 不是 3 的倍數,則做A

或者如果 a 是 5 的倍數,則做A

int a;
cin >> a;
if (a % 5 == 0 || (a % 2 == 0 && a % 3 != 0)) {
	// 其實我不知道我這裡要放甚麼
} 

小小實作

只使用一行 if 判斷閏年

int a;
cin >> a;
if (a % 400 == 0 || (a % 4 == 0 && a % 100 != 0)){
	cout << "leap year" << endl;
} 
else cout << "normal year" << endl;
return 0;

提示:組合條件

ascii、字元與字串

你知道嗎,online gdb可以發出聲音喔

char

可存放一個字元

 

char a='O'
cout << a << endl; //O

"  " 與 '  '

嘗試打出這個程式,你會發現......

char a="O";

因為 " " 裡可以包多個字元,並且 " " 自身的資料型態與 char 不同,是 char 無法存儲的。

而 ' ' 裡包的一定要是單個字元,資料型態即為 char

順帶一提,一個全形符號與中文字占三個字元

多個字元(字串)

那 " " 應該要用哪個資料型態存?

string a="WOW";

String 可以存儲多個字元

並且 string 不能存儲用 ' ' 包起來的單個字元

string a='O';
string a="O";

ascii

ascii code使用在早期美國的通訊中

每個字元對應一個二進位編碼,電腦才看得懂

ascii表就是為了固定一套編碼,防止因為出現多個編碼導致電腦混亂的狀況出現

int('A')

int a;
a = int('A');
cout << a << endl;//65

int() 能夠將括號中的值轉成 int

而將字元轉成 int,就能得到其對應的 ascii code

禮拜五的密碼學應該會提到凱薩加密

將每個字元按照字母表往前或往後移動固定單位

方法就是將每個字元的 ascii 值改變一個固定數值

移動距離為 3 的情況

控制字元

回想剛剛的 ascii 表

是不是少了甚麼?

32 以前的 ascii code 呢?

<32、127 的 ascii 對應的是控制字元

而 ascii code 7 是響鈴

這就是讓 gdb 發出聲音的方法

叮~~

char a;
a = char (7);
cout << a << endl;

char() 可以將括號內的值轉成字元型態

而 char(7) 即是將 ascii code 轉成字元

將其輸出你就能聽到響鈴聲

順帶一提,'\n'是換行

ascii code 為 10

cout << '\n';

再順帶一提,windows的enter是'\r\n'

ascii code 分別為 13,10

游標回到行首,並且換行

好用標頭檔

還醒著嗎

標頭檔

講義最開始有講 iostream 這個標頭檔

他可以幹嘛?

透過 iostream 的引入,編譯器得以識別 cin 為某某種類型的標準輸入物件,並利用相關的運算子重載與函式來完成輸入邏輯

可以試試把 iostream 拔掉之後做 cin cout 的動作

你會發現他報錯了

可以簡單理解為:引入 iostream 後,cin cout 的動作才能順利進行

#include <algorithm>(等一下講)

int n;
int a[n];
sort (a, a+n);

將陣列由小到大排序

int n, target = 3;
int a[n];
lower_bound(a, a+n, target);

取得陣列中第一個大於等於target 值的位置(限陣列為以排序狀態)

int n, target = 3;
int a[n];
upper_bound(a, a+n, target);

取得陣列中第一個大於target 值的位置(限陣列為以排序狀態)

int a = 3, b = 5;
cout << min (a, b) << endl; //3
cout << max (a, b) << endl; //5

min( , ) 獲取兩值中較小的一個

max( , ) 獲取兩值中較大的一個

#include <cmath>

long double pi = 3.14159365358979323
long double a = 90 * pi / 180; //弧度
cout << sin(a) << endl; //正弦,輸出為1

三角函數

int a = 10;
cout << log(a) << endl; //2.30259......

log(以 e 為底)

e = 2.718281828...

int a = 4;
cout << sqrt(a) << endl;//2

根號

int a = -3;
cout << abs(a) << endl;//3

絕對值

float a = 4.5
cout << floor(a) << endl;// 4
cout << ceil(a) << endl;// 5
cout << round(a) << endl;// 5

向下取整

向上取整

四捨五入

#include <utillty>

int a = 3, b = 5;
swap(a, b);
cout << a << ' ' << b << endl //5 3

交換括號內的兩個值

#include <bits/stdc++.h>

哈哈你剛剛都白學了

一次引入C++中幾十個常用的函式庫

包含但不限於上述的三個函式庫
(只適用在競賽中,不適用在專案製作)

阿記得空格

​然後不要main (100)

陣列

第一個是第零個

陣列是甚麼

像是一個橫向的櫃子,存放著多個值

一個位址對應一個值

值(value)

位址(index)

0
1
2
3
4
1
2
3
4
5

一個位置存放一個值,像櫃子一樣

位置是從0開始!!!!!

此陣列的第 0 個位址,存放的數字是 1

人通常會將最左邊的當作第一個

但是電腦是從 0 開始,這很重要

int a[5];

宣告

int 為此陣列中值的資料型態

a 為此陣列的名稱

[5] 為此陣列的大小

int a[5] = {1, 2, 3, 4, 5};

大括號為此陣列內容,可加可不加 

int a[5] = {1, 2, 3, 4, 5};
cout << a[0] << endl; //1
cout << a[4] << endl; //5

記得,陣列的位址是從 0 開始

同時結尾的位址是其大小減一

稱為 0-based

int a[5] = {0};
int a[5] = {1, 2, 3, 4, 5};

同樣的,int 的資料也要歸零

歸零的程式長這樣

迴圈

迴圈是甚麼?

假設我今天想要輸出一個陣列裡的所有值

int a[5] = {1, 2, 3, 4, 5};
cout << a[0] << ' ';
cout << a[1] << ' ';
cout << a[2] << ' ';
cout << a[3] << ' ';
cout << a[4] << ' ' << endl;

但如果陣列長這樣呢?

int a[30000] = {......};

總不可能要寫 30000 次 cout 吧?

這時候就需要用到迴圈,迴圈可以方便的做重複執行的動作

FOR 迴圈

int a[30000] = {......};
for (int i = 0 ; i < 30000 ; i++) {
	cout << a[i] << ' ';
}
cout << endl;

for 裡面要放三個東西

1. 宣告一個計數器變數

2. 設定一個條件,在這個條件成立時,繼續迴圈

3. 每次迴圈重新開始時,要做的事情(通常是計數器 +1)

每個東西用分號隔開

i++ 即為 i = i + 1

這就是 C++ 中 ++ 的意義

FOR 迴圈

int a[30000] = {......};
for (int i = 0 ; i < 30000 ; i++) {
	cout << a[i] << ' ';
}
cout << endl;

迴圈進行的流程:

計數器宣告

確認條件是否成立

進行迴圈

結束後回到開頭

計數器 +1

確認條件是否成立

進行迴圈

......

再提醒一次,陣列的位址是從 0 開始

最後一個的位址是大小減一

所以計數器的初始值為 0

條件是 <30000(到 30000 時離開迴圈)

int a[30000] = {......};
for (int i = 0 ; i < 30000 ; i++) {
	cin >> a[i];
}
for (int i = 0 ; i < 30000 ; i++) {
	cout << a[i] << ' ';
}
cout << endl;

當然,輸入時也需要迴圈

WHILE 迴圈

有聽過 3n+1 問題嗎?

給定一個正整數,如果是奇數,則將此數字 *3+1

如果是偶數,則將此數字 /2

重複進行,會發現每個數字到最後都會變成 1

進入 1 -> 4 -> 2 -> 1 的循環

怎麼用 while 迴圈實作?

while(條件) {
	//執行的程式
}

WHILE 迴圈

while() 的括號中放的是條件

符合條件時則繼續進行此迴圈

while(條件) {
	//執行的程式
}
int a;
cin >> a;
while (a != 1) {
	cout << a << ' ';
	if (a % 2 == 1) a = a * 3 + 1;
    else a = a / 2;
}
cout << a << endl;

當 a 不等於 1 時,繼續迴圈

換言之,當 a 等於 1 時,離開迴圈

小實作

給定一個正整數 n,作為陣列大小

給定一個長度為 n 的整數陣列 a

給定兩個非零整數 l, r,l <= r

求此 l~r此區間內(包含 l 與 r)所有數字的總和

0<=l,r<=n-1,也就是說 l 與 r 為 0-based

範例:

input:

5

1 2 3 4 5

1 2

ouput: 5

小實作答案

int n;
cin >> n; //輸入n
int a[n]; //宣告陣列 a,大小為n
for (int i = 0 ; i < n ; i++) {
	cin >> a[i]; //輸入整個陣列
}
int l, r;
cin >> l >> r; //輸入 l 與 r
int sum = 0; //記得宣告時要歸零,否則 sum 初始值會亂跑
for (int i = l ; i <= r ; i++) {
	sum = sum + a[i]; //加總
}
cout << sum << endl; //輸出答案

buffer overflow

int a[5];
cout << a[5]; //???

會發生甚麼事呢?

陣列是存取在記憶體當中

而陣列長度只有 5 ,沒有 a[5]

所以當編譯器在記憶體中抓不到 a[5]的時候,他就會亂抓,導致輸出一個隨機的數字

int a[5];
cout << a[10000]; //???

如果是這種更極端的情況呢?

報錯,回傳 code 139

當你看到 code 139 時,記得檢查迴圈條件是不是設錯了

continue 與 break

for (int i = 0 ; i < 10 ; i++) {
	if (i < 5) continue;
    cout << i << ' ';
}
for (int i = 0 ; i < 10 ; i++) {
	if (i > 4) break;
    cout << i << ' ';
}

continue:

不執行 continue 之後的程式碼,跑完一次迴圈到開頭

output:

5 6 7 8 9

break:

離開迴圈

output:

0 1 2 3 4

EOF(end of file)

輸入直到讀取到檔案結尾(end of file)

int a;
while (cin >> a) {
	//do something
}

每次輸入時,都會進行迴圈

讀取到檔案結尾時,會被視作 -1

此時就回離開迴圈

do while

do {
	//do something
}
while (條件);

先執行迴圈,再判斷要不要繼續進行

while 後面記得加分號

結束啦 :D

C++基本語法

By 硼/Boron

C++基本語法

  • 176