Try   HackMD

CPP基礎.Lesson 3

字元

什麼是字元

字元包含了:

  • 數字:1234567890
  • 大寫英文字母:ABCDEFG…
  • 小寫英文字母:abcdefg…
  • 符號:#$%&()*+,-./:;<=>?@…
  • 特殊字元:
    • 換行:\n
    • Tab:\t

宣告一個字元

//宣告
char c;

//賦值
c = 'a';

//初始化
char c = 'a'

輸入輸出

char c;

cin >> c;
cout << c;

怎麼輸出單引號'跟反斜線\

cout << ''';
cout << '\';
//error: missing terminating ' character

跟其他特殊符號一樣,在前面加反斜線

cout << '\''; //output: '
cout << '\\'; //output: \

電腦怎麼用0跟1表示字元

ACSII編碼

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

整數和字元間轉換會遵守ASCII編碼

int c1 = 'a';
cout << c1 << endl; //output: 97
 
int c2 = 97;
cout << char(c2) << endl; //output: a
 
char c3 = 97;
cout << c3 << endl; //output: a
 
char c4 = 'a';
cout << int(c4) << endl; //output: 97

小練習

用迴圈印出a~z (ascii code 97~122)
(X) cout << “abcdefghijklmnopqrstuvwxyz”

既然字元是透過數字表達
字元與字元間、字元與數字間也可以運算、比大小
例如:‘Q’ - ‘P’, ‘p’ + 1, ‘q’ - 2

char c = 'a';
cout << c << endl; //output: a

c += 2;
cout << c << endl; //output: c

c -= 1;
cout << c << endl; //output: b

cout << 'z' - c << endl; //'z' - 'b' = 122 - 98 = 24

cout << 'c' + 'A' - 'b' - 'B' << endl; //99 + 65 - 98 - 66 = 0

bool a = 'a' < 'b'; //true, 97 < 98
bool b = 'A' < 'a'; //true, 65 < 97

一維陣列

給你 1000 根棍子,老闆叫你
第 1 根棍子跟第 1000 根棍子接起來
第 2 根棍子跟第 999 根棍子接起來

第 500 根棍子跟第 501 根棍子接起來
因為要趕著出貨的關係,你已經等不及想知道這些棍子接起來長度
於是你決定寫一個程式算好答案

簡單:
宣告1000個int
cin 1000次
用for迴圈相加
cout 500次
趕快打開你心愛的 IDE 開始敲鍵盤

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

偉大的Code就此誕生

Array

  • 多個性質相同的變數
  • 用索引(index)來存取每一個變數
//宣告
//資料型態 陣列名稱[陣列大小];
int arr[100];
double cpsd[2020];
char password[7122];

宣告時陣列大小必須放常數

可以參考這個

//不標準寫法
int n;
cin >> n;
int arr[n];
/*
變數大小陣列
不是 C++ 的正確語法!
只有部份編譯器支援
千萬不要這麼寫!
以後會教其他正確的寫法喔(大概吧)
dynamic memory allocation
std::vector
 */
//初始化
//陣列跟變數一樣,沒有初始化的時候無法確定他的值 (garbage value)
int a[5];                       // 沒有初始化
int b[5] = {0, 1, 2, 3, 4};
int c[5] = {0, 1};              // 自動補零
int e[100] = {};                // 自動補零(通通歸零)
int d[] = {0, 1, 2, 3, 4, 5};   // 自動計算長度

//存取變數
//在中括號裡面放索引值(index)
//索引值可以是變數或常數
int arr[10];                    // 宣告常數大小的陣列
int x = 2, y = 3;
arr[0] = arr[x] + arr[y];       // 索引可以是變數或常數

索引值從0開始

int arr[3];
arr[0] = 123;   // ok
arr[1] = 456;   // ok
arr[2] = 789;   // ok
arr[3] = 8787;  // GG
有關未初始化的補充 英文不好+懶得翻譯=自己看原文

Here are the specific C 2018 rules about that:

  • The so-called “value” of an uninitialized object is indeterminate, per 6.2.4 6 for objects with automatic storage duration without variable length array type, 6.2.4 7 for those with variable length array type, 7.22.3.4 2 for objects allocated with malloc, 7.22.3.5 2 for additional space allocated by realloc, and 7.22.3.1 2 for aligned_alloc. Other objects, such as those with static storage duration, are initialized.
  • Per 3.19.2, an indeterminate value is “either an unspecified value or a trap representation”.
  • Per 3.19.3, an unspecified value is a “valid value of the relevant type where this document imposes no requirements on which value is chosen in any instance”.

This means that in each instance an object is used, the C standard does not impose any requirements on which value is used for it. It is not required to be the same as a previous use. The program may behave as if it does not hold any fixed value. When it is used multiple times, the program may act as if it has a different value each time. For example, the C standard would allow printf("%d %d %d\n", a, a, a); to print “34 -10200773 2147483204”.

A way this can happen is that, while attempting to compile the code int a; printf("%d %d %d\n", a, a, a);, the compiler has nowhere to get a from, because it has never been given any fixed value. So, instead of generating useless instructions to move data from uninitialized memory to where the arguments are passed, the compiler generates nothing. Then printf gets called, and the registers or stack locations where the arguments are passed contain whatever data they had from earlier. And that may well be three different values, which printf prints. So it looks to an observer of the output as if a had three different values in printf("%d %d %d\n", a, a, a);.

(In addition, using the value of an uninitialized object with automatic storage duration that has not had its address taken is explicitly undefined behavior, because 6.3.2.1 2, about converting an object to its value, says “If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.” So, when there is an object meeting these criteria, using its value can break the program completely; it might not only have different values at different times, but the program might abort, go down a branch different from what you expected, not call printf when there is a printf in the source code, and so on.)

宜搭配迴圈服用

e.g. 輸入 n 個數字,把他存到一個陣列裡面

int n = 3, arr[3];
for(int i = 0; i < 3; i++)
    cin >> arr[i];

字元陣列C-style字串

一個字元一個字元的說話很累耶
把多個字元串在一起

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
字串

0 1 2 3 4
'c' 'p' 's' 'd' '\0'
  • '\0'就像是字串的句點
  • 在輸出字元陣列時,會輸出到'\0'為止
  • 也就是說,'\0'後的字元不會被輸出

會輸出甚麼?

char str[100] = "HelloWorld";
str[5] = '\0';
cout << str;
0 1 2 3 4 5 6 7 8 9 10
'H' 'e' 'l' 'l' 'o' 'W' 'o' 'r' 'l' 'd' '\0'
0 1 2 3 4 5 6 7 8 9 10
'H' 'e' 'l' 'l' 'o' '\0' 'o' 'r' 'l' 'd' '\0'

=

char a[6] = "abcde";
char b[6];
b = a;
cout << b << "\n";
//error: invalid array assignment

你可以這樣:

char a[6] = "abcde";
char b[6];
for(int i = 0; i<6; ++i)
    b[i] = a[i];
cout << b << "\n"; //output: abcde

字串陣列

char arr[3][4] = {"abc", "def", "ghi"};
//3個字串
//每個字串包含'\0'最多4字元
0 1 2 3
0 'a' 'b' 'c' '\0'
1 'd' 'e' 'f' '\0'
2 'g' 'h' 'i' '\0'

小練習

a011: 00494 - Kindergarten Counting Game


函數

從輸入值集合

X 到可能的輸出值集合
Y
的函數
f
記作
f:XY
X
Y
的關係,滿足如下條件:

  1. f
    是完全的
  2. f
    是多對一的
  1. f 是完全的
    對輸入值集合
    X
    裡的任何元素
    x

    輸出值集合
    Y
    裡存在
    f(x)

  2. f 是多對一的
    允許
    f(x)=f(y),xy

    不允許
    f(x)=a,f(x)=b,ab

東西進去,答案出來

國中數學課本裡的例子:

函數:販賣機
輸入:錢 + 想買的飲料的編號
輸出:飲料 + 找零 or 找零(錢不夠)

Your first function

int main() {
    return 0;
}

函數的長相

一般的函數大概會長這樣:

回傳的型態 函數名稱(參數 1, 參數 2, ...) {
    各種處理
    return 回傳值;
}

函數宣告/呼叫

要使用定義好的函數,我們只要看清楚函數宣告的第一行:

int add(int a, int b) ...
int x = add(3, 4); // x = 7
int y = add(3, add(4, 5)); // y = 12

函數重載(Function Overloading)

相同名稱的函數但傳入的參數不同

int add(int a, int b){
    return a + b;
}
int add(int a, int b, int c){
    return a + b + c;
}

C++ Library

高中生熟悉的函數們

#include <cmath>
std::log10(100);  // 2    log以10為底100
std::abs(-3);     // 3    絕對值
std::exp2(10);    // 1024 2的幾次方
std::pow(3, 2);   // 9    3的2次方
std::sqrt(9);     // 3    根號9 = pow(9, 0.5)
std::sin(0);      // 0    
std::acos(-1);    // 3.14159,(arccos,反餘弦函數)

好用的函數們

#include <algorithm>
std::min(2, 3);   // 2
std::max(2, 3);   // 3
std::swap(x, y);

函數回傳值

  • 有些函數沒有回傳值(void)
int x = 3, y = 5;
std::swap(x, y);  // x = 5, y = 3
  • 有些函數有回傳值
  • 語法上可以直接當他是個值
std::min(3, std::max(1, 2));
// == std::min(3, 2)
// == 2 
  • 函數呼叫時參數的處理順序沒有規範
  • 避免會相互影響的東西出現在參數欄位
  • 否則結果為何端看你的編譯器
int x = 3, y = 5;
std::min(y = x, x = y); // what happens?

字串常用函數

複習

char a;
//a 是字元,亦是一個 0~255 的整數

char a[] = "...";
//a 是字串 (C-style),亦是字元的陣列

//輸入
char a[10];
std::cin >> a; //讀到空白字元 (會捨棄空白字元)
std::cin.getline(a, 10); //讀到換行字元 (會捨棄換行字元)
std::cin.getline(a, 10, 'x'); //讀到x (會捨棄x)


//輸出,會依序輸出 a 的字元,直到 0 (\0)
char a[] = "ABCD";
std::cout << a; //ABCD
a[2] = 0;
std::cout << a; //AB
  • 這裡介紹 C-style 字串的函數
  • 可以在 <cstring> 裡找到
  • 勿與 C++ 字串 string 搞混
//字符分類
isalnum() // is alphabet or number, if true return 非0值
islower() // is lowercase
isdigit() // is digit
isblank() // \n, \t, \0, etc.

//字符操作
char a = std::tolower('A');  // a
char b = std::tolower('b');  // b

//數值轉換
int c = std::atoi("34");     // a = 34
int d = std::atoi("3.4b");   // b = 3
int e = std::atoi("abc3");   // c = 0

//字串操作
char f[5] = "AB";
std::strcpy(f, "CD");        // f = "CD" 複製
char g[5] = "AB";
std::strncpy(g, "EFG", 1);   // g = "EB" 複製某個數量
char h[5] = "AB";
std::strcat(h, "CD");        // h = "ABCD" 接在一起
char i[5] = "AB";
std::strncat(i, "EFG", 1);   // i = "ABE"  把某個數量接在一起

//字串長度檢測
std::strlen("ABC");          // 3
//若所指向的字元數組中無空字元('\0'),則行為未定義


//字串比較
std::strcmp("ABC", "AB");    // 1, note that it's > 0
//若不是指向空終止位元組字元串的指標
//(指針(簡體中文用法) 英:pointer),
//(即其中有無結尾字串),則行為未定義

//比較兩個字串中指定長度內的字元是否相同
std::strncmp("ABC", "AB", 2);// 0
//若出現越過結尾的訪問,則行為未定義
//若為空指針,則行為未定義。


//數組操作
char a[] = "dirty stuff", b[12];
memcpy(b, a, min(sizeof(a), sizeof(b))-1);
//儘可能把 a 複製到 b
memset(a, 'a', sizeof(a)-1);
//把 a 設成 "aaaaaaaaaaa"

任何程式語言的學習都沒有一步登天的方式,一步步來慢慢學

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

小murmured:
偶覺得神奇把大家想的很神奇
也太難ㄌ吧🥴
水獺

回家作業

a248: 新手訓練 ~ 陣列應用