~~ 2019/10 更新:終於把它寫完整了TvT 寫這篇寫了三年哈哈。補了彈出視窗, cin/cout, endian, stringstream. ~~
以下是我整理出來的課外筆記,會盡量避免正規 C/C++ 課堂會教的內容~
跟大家分享,希望能長知識喔~!
Input/Output
1. getch(), getche()
大家都有聽過 getchar(),但 getch() 就比較少人聽過了~
它的功能是,按下去鍵盤按鍵時,不需要按 Enter 就會直接讀進去,而且螢幕不會顯示!
在製作CMD小遊戲時還滿實用的,eg. 按w往上,按a往左,不用每個指令再按Enter 才能送出去
<conio.h> -> getch(): 不需按Enter, 螢幕不會顯示
getche(): 不需按Enter, 螢幕會顯示
2. kbhit()
可以偵測鍵盤在這瞬間有沒有被按下去~ 也是在 <conio.h> 裡面
這在製作小遊戲,特別是動態的互動式程式時,也很實用,
eg. 包在迴圈裡面,每次都檢查 if( kbhit() ),當有按按鍵時執行一些動作 (例如人物移動)
3. ungetc()
從stdin 讀進來後是不是就回不去了?
其實是可以再吐回去的,一次一個字元: ungetc(x, stdin)
C++也有C++ 的方法: cin.unget(),不需要指定回收什麼東西
4. 顯示兩位元寬的文字:%c%c
eg. 想要儲存一個磚塊 █,會需要兩個char,
char a[3] = "█";
想要印出一個它時,會需要兩個 %c%c 緊貼在一起,才能"拼湊"出 █ 喔!
printf("磚塊 = %c%c", a[0], a[1]);
註:中文字都是兩位元寬的喔~
5. Windows 彈出式視窗
首先 include <windows.h>,然後呼叫 MessageBoxW 函式,eg.
LPCWSTR text = L"Hello world";
LPCWSTR title = L"Hi";
MessageBoxW(NULL, text, title, 0x40);
注意,它需要先是 wide string,然後再轉成 LPCWSTR。
至於最後一個參數,填不同值會有不同效果喔!
最後一位是按鈕,0=OK,1=OKCancel,4=YesNo,3=YesNoCancel。
倒數第二位是圖示,0=無,1=Stop,2=Question,3=Warning,4=Info。
(eg. 0x23 會有三個按鈕,然後圖示是問號)
6. cin / cout 比 printf / scanf 還要慢?
其實理論上應該要一樣快的,但因為有一些額外的機制,造成速度被拖慢了!
主要的罪魁禍首是 flush,以及 C++ 跟 C 的一個同步機制。(請見寫得很好的 這篇)
讓 cin / cout 變得跟 printf / scanf 一樣快的方法:
1) 在開頭加上:ios_base::sync_with_stdio(false);
2) 不要使用 endl,一律改用 cout << "\n";
3) 在開頭加上:cin.tie(NULL);
程式運作底層
1. Function 是右到左參數傳遞
這點老師可能會提到,就是當呼叫 foo(c++, c++, c++) 時,
void foo(int a, int b, int c){ ... } 函數裡面的 a 會最大,c 會最小。
2. Big-endian & Small-endian
這點老師可能也會提到。Big-endian 比較像是「我們想像中的」變數在記憶體中的樣子,
例如 0x01020304,在記憶體中,從低位置到高位置會是 [01]-[02]-[03]-[04],
但是 Little-endian 正好相反,會是 [04]-[03]-[02]-[01]。
今天大多數的 CPU 都是 Little-endian,理由是效率比較好。
不信的話,你可以試試以下程式碼:
int x = 0x01020304; char c[4]; FILE* f = fopen("num", "rwb"); fwrite(&x, sizeof(int), 1, f); fread(c, sizeof(char), 4, f); printf("%d%d%d%d\n", c[0], c[1], c[2], c[3]);
操作其他檔案
1. 跟 CMD 合作
C/C++ 可以直接在 CMD 執行指令,只需要包在 system("...") 裡面就可以了,
eg. system("shutdown -s"), system("cls") -> 清空螢幕(可以拿來製作動態使用者介面)
2. 呼叫其他程式同步執行 (Multi-Thread)
呼叫其他程式還不簡單?可以直接透過 CMD 呼叫: system("D:\\folder\\programB.exe");
但是要做到 "同步" 執行,就需要 multi-thread(多執行緒) 了~
在 <process.h> 底下,可以用 spawnl(),
例如: spawnl(P_NOWAIT, "D:\\folder\\programB.exe", "D:\\folder\\programB.exe", "13", "apple", NULL);
P_NOWAIT 就是不用等它跑完,第二個參數是exe 的 path,第三個以後是 argv[0], argv[1], argv[2], ....,最後一個固定都是填 NULL,
它的回傳值,就會是程式的回傳值,如果順利執行就是 0。
更多參數可參考: 這篇
3. 取得資料夾名稱
透過 CMD 是有能力把資料夾名稱列表輸入進檔案裡的,eg. dir/b > D:\folder\direct.txt
然後執行完指令,再用讀檔的方式來取得資料夾資訊!
但其實也可以省去 CMD 當中間人,直接取得資料夾資訊喔,
<dirent.h> 底下,先宣告代表資料夾的指標 DIR* dir; 以及讀進相關資訊的指標 struct dirent *info;
再來有點像是C 的開檔讀檔,dir=opendir("D:\\folder\\2016example"),之後就可以用它來讀資訊啦,info=readdir(dir),
可以顯示檔名 info->d_name,或者檔名的長度 info->d_reclen,最後記得要關閉資料流 closedir(dir);
(不過不能有中文的資料夾名稱,其實不是很好用XD)
C++功能學不完
1. C++ Container 不會 buffer overflow
如題,Cpp 的容器都是動態的 (eg. string, vector, list, ...)
所以基本上還滿安全的,想要透過 buffer overflow 來駭進 C++ 程式沒那麼容易嘿嘿~
不同 container 特性以及有什麼功能,請多多參訪 cppreference
2. 如何清空 stringstream?
你可能會想說,清空 stringstream 還不簡單,不就是 ss.str("") 就好了嗎?(假設 ss 是你的 stringstream)
NO!因為當你把 stringstream 讀完了以後,你會發現你無法再寫新的資料進去了!
所以正確的方式是:ss.str(""),ss.clear() 才對。
3. Iterator
不曉得老師會不會教這個,基本上C++ 每種容器都含有各自的 iterator 給你用,
iterator(疊代器) 是類似指標的概念,宣告時: list<string>::iterator itS; 注意它需要註明是哪種容器的 iterator 喔~
使用時可以 for(itA=v.begin; itA!=itB; itA++)
4. Insert/Erase
很多C++ 的容器都有 insert 和 erase 功能,所以 vector 也可以拿來當 list, queue 來使用 XP
用法都是需要透過 iterator,假設 itV 指向 vector 第五個元素,那麼可以 v.insert(itV, 3),就會在第五個元素之前插入一個 3
假設 itV2 指向 vector 第八個元素,那麼 v.erase(itV, itV2),就會移除元素 5, 6, 7,因為不會包含最後一個 [A, B)
不過,如果你講求程式效率的話,最好了解一下他們的時間複雜度(complexity),有些容器insert/erase 會花不少時間的喔!可以去cppreference 查詢~
5. freopen / rdbuf
在 C,可以使用 freopen("input.txt", "r", stdin),讓 file stream 如同 stdin 一樣被讀近來,
所以之後所有的 scanf 都會從檔案讀進來
在 C++,可以使用 cin.rdbuf( fin.rdbuf() ),將 fin 導向 cin,也會有同樣的效果
如果要把 cerr 關掉,也可以 cerr.rdbuf(NULL),讓它導向 NULL。
留言列表