--- tags: 2021CRC Extra Resources title: 社課額外補充教材:變數與運算 --- # 社課額外補充教材 : 變數與運算 如果你覺得社課還算輕鬆,這裡提供你一些額外的補充教材。 這些是我們在備課的時候覺得稍微進階,或是篇幅太多,而沒有放在正式社課的內容。 --- 這裡有目錄,左邊也有,可以跳到自己想看的地方 [TOC] --- 建議必看的地方 : * 記憶體的觀念 * 有關字串的補充(第二部分) * 字元運算 --- ### 記憶體的觀念 記憶體是我們寫程式的好朋友,當我們宣告一個變數,就等同於在記憶體上畫出一個空間, 讓這個空間存放變數的值。所以你可以把變數理解成 **"一塊儲存可變動的值的記憶體空間"**。 記憶體在之後的指標跟資料結構的時候都會再提到, #### 各種資料型態的記憶體空間 整數 * int、unsigned int : 4 bytes * long long、unsigned long long : 8 bytes 浮點數 * float : 4 bytes * double : 8 bytes 字元跟字串 * char : 1 byte * string : 長度+1 bytes --- ### 字元與ASCII的範例Code ### 字元運算 整數、大寫字母、小寫字母的ASCII Code 分別都是連續的 ```cpp= char a = 'A'; a = char( a + 1 ); // a 的值由 A 變成 B // 當然你也可以寫 char( int(a) + 1 ) ``` ```cpp= #include<iostream> using namespace std; int main(){ char a = '0'; cout << a << endl; cout << (int) a << endl; a += 1; // a == '1' if (a == '1') cout << "Yes" << endl; cout << a << endl; cout << a + 1 << endl; } ``` --- ### 有關字串的補充(第一部分) 其實字串是由字元所組成,最後會在結尾加上 '\0' 表結束 ```cpp= #include<iostream> #include<string> int main(){ char a[6] = {'h','e','l','l','o','\0'}; std::string b = "hello"; //和a一樣都是"hello" char c[6] = "hello"; //也可以這樣寫 char d[5] = "hello"; //但不能這樣寫,記住要連'\0'也算進去 char e[] = "hello"; //也可以這樣寫,e的長度是6 } ``` 那為什麼要用 std::string ? 因為 std::string 可以這樣 ```cpp= #include<iostream> #include<string> int main() { std::string b; std::cin >> b; //可以輸入任意長度的字串 std::cout << b; char a[] = "hello"; //a 的長度被限制在6 std::cin >> a; //輸入超過6-1個字會爆掉 } ``` 怎麼做到的? 查查指標陣列吧,但看看就好 References: [TutorialsPoint](http://www.tutorialspoint.com/cprogramming/c_strings.htm) [W3School C++ : String](https://www.w3schools.com/cpp/cpp_strings.asp) 慎入: [Microsoft Docs](https://docs.microsoft.com/zh-tw/dotnet/api/system.string.-ctor?view=net-5.0) --- ### 有關字串的補充(第二部分) : stoi and to_string 上課的時候我們有給過這張圖片: ![](https://i.imgur.com/DUDdXPm.png =75%x) 其中有兩個函式 stoi(s) 跟 to_string(i)是在比較新的C\++版本,從**C\++11**之後才出現的。而因為DevC\++的默認C\++版本是很古老的C\++98,所以如果需要使用這兩個函式實作時,你會發現 compiler 一直跟你說: **"to_string" / "stoi" is not declared in this scope.** 所以如果需要使用這兩個函式,我們需要把DevC\++的版本提高。 依序點開**Tools -> Compiler Options**,並在General的第一個方框內打上 **"-std=c++11"** ,即可把你的C\++版本手動升級到C\++11囉! ![](https://i.imgur.com/vmhz5Xu.png =70%x) --- ### 有關浮點數的補充 - 我想四捨五入怎麼做? 為您隆重介紹,std::setprecision!! ```cpp= #include<iomanip> //setprecision要用的函式庫 #include<iostream> int main(){ std::cout << std::setprecision(3); //注意!此效果是全域的 //3表示的是總長度 std::cout << 3.1415926 << '\n'; //結果會是 3.14 std::cout << 13.75 << '\n'; //結果會是13.8 } ``` 要怎麼只動到小數點後的位數? ```cpp= #include<iomanip> //setprecision要用的函式庫 #include<iostream> int main(){ std::cout << std::fixed << std::setprecision(3); //加在setprecision前後都沒關係,效果一樣是全域 std::cout << 13.14159; //結果會是13.142 std::cout << 1.2; //結果會是1.200 } ``` 沒錯,加了fixed後會自動補0 那要怎麼還原? ```cpp= #include<iomanip> //setprecision要用的函式庫 #include<iostream> int main(){ std::cout << std::fixed << std::setprecision(3); //加在setprecision前後都沒關係,效果一樣是全域 std::cout.unsetf(ios::fixed);//這能將fixed效果關掉 //不過setprecision並不是能關的函式,只能還原成預設值 std::setprecision(6); //預設是6 //以下的輸出即是預設輸出 std::cout << 3.1415926; //結果會是3.14159 std::cout << 13.1415926; //結果會是13.1416 } ``` 除此之外,有關輸出對齊的功能也一併放在iomanip 以下是解說 [Microsoft Doc](https://docs.microsoft.com/zh-tw/cpp/standard-library/iomanip-functions?view=msvc-160) --- ### 前綴詞 - 常數 const - 正數 unsigned - 加長 long - 變短 short ```cpp= const int a = 1; //a之後就不能在程式中任意更改了 unsigned int b; //b的長度比int大1,從0 ~ 4,294,967,295 long long (int) c; //前一個long是前贅詞,後一個long是資料型態 short (int) d; //比int還短,能存的值更少,最多到32767 ``` #### const比你想的常見 ```cpp= 1 的型態是 const int '1' 的型態是 const char "1" 的型態是 const char[2] 1.0 的型態是 const double 1.0f 的型態是 const float ``` #### 不過沒有所謂的 const bool true和false是保留字 --- ### 後綴詞 這要從auto說起 ```cpp= auto a = 1 //a的型態是int auto b = '1' //b的型態是char ``` 看出來了嗎? auto會隨著後面指定的值轉換型態 可是我想讓a變成long long怎麼辦? 這時候就是後綴詞出馬的時候了 #### 各個資料型態的後綴 * u : 表示 unsigned * l : 表示 long 或 long double * ll : 表示 long long * ul : 表示 unsigned long * ull : 表示 unsigned long long * f : 表示float #### 用法 ```cpp= 1l //型態為long 1.0l //型態為long double 1u //型態為unsigned int 1.0f //型態為float 1.0 //依編譯器不同,可能為float或double //然而大部分都是double ``` #### 其他類似的用法 * 十六進位要加前導0x ```cpp= 0xA1 //等於161 ``` --- ### 有關左右值 [Microsoft Docs](https://docs.microsoft.com/zh-tw/cpp/cpp/lvalues-and-rvalues-visual-cpp?view=msvc-160) 說人話 ```cpp= int a; 4 = a; //不合法,4是右值不能放左邊 const int b; b = 5; //不合法,const是不能改的左值(non-modifiable lvalue) a = b; //合法 ``` --- ### \++的好玩之處 : a\++ 和 \++a 差別 ```cpp= int a = 4; int b; b = a++; std::cout << "a = " << a << " , "; std::cout << "b = " << b; //結果是 a = 5 , b = 4 int c = 4; int d; d = ++c; std::cout << "c = " << c << " , "; std::cout << "d = " << d; //結果是 c = 5 , d = 5 ``` a\++會先回傳a的值,再把a + 1 \++a會先把a + 1,再回傳a的值 說人話: a\++是先運算後再加 , \++a是先加後再運算 猜猜看: ```cpp= int a = 10; int b; int c = 5; b = ++a - c++; std::cout << b++ + a++ + c; ``` 答案是15 * 6 - 10 / 2 + 7 - 69的值喔 ##### 當然 a-\- 還有 --a 是相同的概念喔 --- ## 有關輸入、輸出 - 為何要用cin/cout? ```cpp= std::cout << "因為" << "cout" << "可以這樣做" << '\n'; printf("當然printf也可以這樣做\n"); std::cout << "但我相信這樣子" << '\n' << "比"; printf("這樣子做\n直觀"); ``` 當然scanf和cin也是相同的概念 - 有人說scanf/printf的速度比較快? 一般情況下不否認,但那是因為cin/cout有比較多的功能 那把那些功能關掉呢? [C++的輸出入cin/cout和scanf/printf誰比較快?](https://chino.taipei/note-2016-0311C-%E7%9A%84%E8%BC%B8%E5%87%BA%E5%85%A5cin-cout%E5%92%8Cscanf-printf%E8%AA%B0%E6%AF%94%E8%BC%83%E5%BF%AB%EF%BC%9F/) ### 為什麼要用endl? 這要提到緩衝區的觀念 從上面的實驗也可以知道緩衝區也拖慢了速度 那為什麼要有緩衝區,或著說,甚麼是緩衝區? ### 緩衝區(buffer) 緩衝區大小:看各電腦系統 Windows是32768個字元 超過時就會直接輸入 在讀取輸入的時候分為三種 1. 字緩衝 2. 行緩衝 3. 全緩衝 --- #### 字緩衝 字緩衝以空格' '為分界,當遇到空格時會將前面的東西寫入程式,再把空格清除。 常用的cin >> , printf都是這樣 --- #### 行緩衝 行緩衝以行'\n'為分界,當用戶按下enter的時候才會把前面的東西寫入程式。 在 C++ 中可以用 getline() 來達到這件事 ```cpp= #include<iostream> #include<string> int main(){ std::string a; getline(std::cin,a); //這意思是把cin中讀取到的東西寫入a那裏 std::cout << a; //a 可以連空格也一起讀取 } ``` --- #### 全緩衝 全緩衝直到buffer滿了之後才會寫入,或是使用特殊的函式強制寫入緩衝區的內容 用法:我不知道 --- #### 所以為什麼要有緩衝區? 因為將讀取到的東西寫入很花時間, 所以比起一個字一個字的寫入,還不如一次寫入多個資料效率較高 --- #### 那有沒有不緩衝的 有,像是cerr就是因為必須要盡快輸出錯誤訊息所以沒有緩衝區 也可以使用cin.get() ```cpp= include<iostream> int main(){ char a = char(std::cin.get()) //因為cin.get()回傳的是ASCII碼,要把它換回字元 std::cout << a; } ``` --- #### 邪教---不用變數的輸入 ```cpp= #include<iostream> int main(){ std::cout << char(std::cin.get()); //只能讀一個字元 return 0; } ``` --- #### 強制清空buffer 可以使用 std::flush 或 std::fflush(stdout) ```cpp= std::cout << std::flush; std::cout << std::fflush(stdout); //這兩行是相同的意思 ``` --- #### 所以什麼是endl ```cpp= std::cout << endl; std::cout << '\n' << std::flush; std::cout << '\n' << std::fflush(stdout); //沒錯這三行是一樣的 ``` --- #### 所以這到底什麼時候會用到? 當有人嘗試要用std::fstream的時候。 std::ofstream中寫到的東西是全緩衝,直到程式結束後才會輸入。 聽不懂嗎 ? 聽不懂沒關係我們**不會**教(絕對不會在正課教)。 有興趣的我們放連結在下面: [Microsoft Doc](https://docs.microsoft.com/zh-tw/cpp/standard-library/fstream?view=msvc-160) [W3School C++ Files](https://www.w3schools.com/cpp/cpp_files.asp) --- #### buffer overflow 自己查不想教 還有這東西現在基本上都有應對方式 ###### 或是問問網管小組的,他們都超電(小聲 ---