網頁
2009年9月25日 星期五
Mail Server
law@gmail.com
law為使用者名稱
gmail.com為主機名稱
Mail server componet
1. MUA ─ Mail User Agent 使用者用以寫信收信的程式。
2. MTA ─ Mail Transfer Agent 負責轉送信的伺服器。(功能是Relay)
3. MDA ─ Msil Delivery Agent 負責將要給本地使用者的郵件分配到使用者信箱中。(Mail)
POP3「Post Office Protocol - Version 3」
支持使用客戶端遠程管理在伺服器上的電子郵件。支援「離線」郵件處理。
Method: 郵件發送到伺服器上,MUA連接伺服器,並下載所有未閱讀的電子郵件。這種離線訪問模式是一種存儲轉發服務,將郵件從server送到terminal上,一旦郵件download,郵件伺服器上的郵件將會被刪除。
Internet Message Access Protocol (IMAP)
類似POP3 但有以下其他功能:
* 支援連線和離現兩種操作模式。
* 支援多個USER同時連線到一個郵箱。
* 支援在伺服器保留訊息狀態資訊。
* 支援在伺服器上存取多個郵箱。
2009年9月10日 星期四
Ant Algorithms
螞蟻可以由蟻穴到食物目的地找到一條最短路線, 它們用的不是視覺, 而是在走過的地方會殘留一種分泌物pheromone, 當以後的螞蟻經過時, 就有較高的機率選擇pheromone濃度高的方向, 因此隨著時間增長, 漸漸螞蟻會走同一路線(亦即最短路線)由蟻穴到食物目的地來回, 利用這種自然界的原理已有效率地解一些最佳化問題(Optimization Problems)。
2009年9月7日 星期一
[轉錄]批次檔
甚麼是批次檔:
在DOS系統下,有三類檔案是可以使DOS執行而進行某些工
作,這三類檔案分別有以下三個不同的副檔名
1. .com (指令檔,命令檔) 例如 Command.com, Edit.com 等。
2. .exe (執行檔) 例如 Copy.exe, Mem.exe 等。
3. .bat (批次檔) 例如 Autoexec.bat 等。
其中 .com 及 .exe 的檔案是用編譯或組合語言寫出,需要較多工夫來學習編寫,一般終端用者 (end-user) 不會花時間來學習編寫這些程式,但 .bat 的批次檔是一連串的 DOS 內部或外部指令 (批次檔的意思是 指令集) ,或是執行程式的主檔名,因此只要懂得這些指令,加上一些簡單的語法,就可寫出批次檔。終端用者利用自己編寫的批次檔,就可使 DOS 執行自己編定的程序。
批次檔是由 DOS 指令組成,因此批次檔有錯誤時,就等於我們在提示號 (C)鍵入錯的指令一樣,螢幕會有 Bad command or file name 的回應。
批次檔必定要是純文字檔案,可使用 DOS 的編輯器(DOS Editor)
或用windows內的 記事本 寫出來,這兩個都是簡單的文書處理器,文稿內不含任何控制碼 (如字款和字的大小,文章闊度等等),這就最適合用來編來批次檔。
批次檔的規格:
1. 批次檔要以純文字寫出,每一項指令要在一行中完成,不能分兩行,每行最多是 127 個字。每一行只能容納一個指令,兩個指令就要分兩行 (或用連結符號)。
2. 批次檔的檔案要用 .bat 為副檔名,除了 Autoexec.bat,主檔名由用者自定,但最好避免和 DOS 指令同名,例如不要用 Copy.bat、Del.bat、Dir.bat、Exit.bat 等等。
3. 批次檔需用 DOS 指令加上特定語次寫成,這些指令包括 DOS 內部及外部指令,語法有以下各項:
call choice echo
for...in...do goto if(if exit)(if not exist)
pause rem shift
指令錯誤會有 "Bad command or file name" 回應,語法錯誤會有 " Syntax error" 的回應。
4. 要 DOS 執行批次檔的指令,只需鍵入主檔名,例如 Menu.bat 或 menu。不過若批次檔的主檔名和 .com 或 .exe 的主檔名相同時,就要鍵入全名,例如批次檔的名稱是 copy.bat 或 edit.bat時,就要鍵入copy.bat 或 edit.bat 才能使電腦執行批次檔的指令,否則電腦會執行 copy.exe 或 edit.com 這些 DOS 指令。
5. 在批次檔執行途中,一起按下 [Ctrl]+[C] 或 [Ctrl]+[Break] 可終止程序。按下這兩個鍵後,螢幕會顯示:Terminate batch job (y/N)?
按 [Y] 會終止這批次檔和回到 DOS 的提示號,按 [N] 則取消[Ctrl]+[C] 指令,批次檔會繼續執行程序。
早期在DOS系統工作下,都可能需要使用批次檔來執行電腦工作,當文字介面的系統逐漸被Windows、Unix系統(圖形介面)所取代時,現今一般使用電腦的人,可能也不是很清楚什麼是批次檔,我們今天就來介紹何謂批次檔。
批次檔是一種文字檔案(ASCII檔案),由一連串的MS-DOS命令所組成的。
批次檔的命名規則與一般檔案相同,但其副檔名固定為".BAT",即"將 一連串的命令"儲存在檔案中,該批次檔的指令可無限制的重覆使用,永遠得到相同的執行結果。究竟批次檔又該如何使用呢?以下介紹批次檔的常用指令。
批次檔的常用指令如下:
*------------------------------------------
一、 ECHO
*------------------------------------------
在正常的情況下,批次檔中的每道命令執行前都會先出現螢幕上。有了ECHO命令,就可以任意控制是否列印出命令列。
用法:在批次檔中鍵入echo on/off ,on:表示要印出命令列,off:則不印出命令列,若要禁止批次檔的命令顯示在螢幕上,則使用隱藏echo off(@ECHO OFF),用法在命令列的開頭上加上@符號
Ex:
@echo off 表示資料隱藏
echo .... 測試ECHO指令
echo .... 請稍後
echo .... 完成.....
結果:
Test Enter
.... 測試ECHO指令
.... 請稍後
.... 完成.....
*------------------------------------------
* 二、 代換參數
*------------------------------------------
代換參數的標記是一個百分號(%)再跟著一個阿拉伯數字,最多可使用10個代換參數,這些變數的值,則由呼叫批次檔的命令所指定。
Ex:
@echo off
echo .... 測試代換參數指令
echo .... 請稍後
echo .... 完成.....
echo %1 %2 %3 %4
結果:
Test TCP EDU NETWORK CENTER Enter
.... 測試ECHO指令
.... 請稍後
.... 完成.....
TCP EDU NETWORK CENTER
*------------------------------------------
* 三、 PAUSE:是暫停批次檔的執行。
*------------------------------------------
當MS-DOS執行到PAUSE時就暫時中止目前的工作,並印出下列訊息:
press any key to contunue ……
Ex:
@echo off
echo .... 測試代換參數指令
echo .... 請稍後
echo .... 完成.....
echo %1 %2 %3 %4
pause
echo %5 %6
結果:
Test TCP EDU NETWORK CENTER Enter E-Mail chan@ms64.hinet.net
.... 測試ECHO指令
.... 請稍後
.... 完成.....
TCP EDU NETWORK CENTER
Press any key to continue ....
E-Mail chan@ms64.hinet.net
*------------------------------------------
* 四、 REM(REMark)用來在批次檔執行中顯示某些訊息
*------------------------------------------
用法是在批次檔中鍵入rem,後面跟著所要顯示的字串
Ex:
@echo off
echo .... 測試代換參數指令
echo .... 請稍後
echo .... 完成.....
rem 下面指令,開始做代換參數
echo %1 %2 %3 %4
pause
echo %5 %6
*------------------------------------------
* 五、 IF
*------------------------------------------
IF命令在批次檔中用來測試特定的條件,以決定是否執行某些命令。絛件的型態分為四種:
////////////////////////////////////////////////////////////////////
(1) IF EXIST
此條件是用來檢查某一檔案是否存在,若存在則條件成立,便執行指定的命令
Ex:
@echo off
if exist test1.bat type test1.bat
////////////////////////////////////////////////////////////////////
(2) IF String1 = = String2(= = 表相等性的比較)
Ex:
@echo off
echo .... 測試IF指令
echo %1
if '%OS%'=='Windosws_NT' goto good
if %1 == a goto first
if %1 == b goto second
if %1 == c goto three
:good
echo 作業系統%OS%
:first
echo 執行first (a)
goto finish
:second
echo 執行second (b)
goto finish
:three
echo 執行three (c)
goto finish
:finish
結果:
Test1 a Enter
a
執行First(a)
執行程式結束
////////////////////////////////////////////////////////////////////
(3) IF ERRORLEVEL
ERRORLEVEL是由MS-DOS所管理的一個系統變數,目的是監視所有錯誤發生的情況。(類似ErrorMessage)
////////////////////////////////////////////////////////////////////
(4) IF NOT
IF NOT是當測試條件不成立時才執行後面命令
Ex:
@echo off
if not exist test1.bat type test1.bat
echo 執行程式結束
////////////////////////////////////////////////////////////////////
*------------------------------------------
* 六、 GOTO
*------------------------------------------
GOTO命令在批次檔中用來轉移控制權,可以指示批次檔跳至某一標記(line label)由一個冒號(:)跟著字元符號所組成的;label也可使用代換變數
Ex:
goto first 或 goto %1
*------------------------------------------
* 七、 FOR
*------------------------------------------
FOR命令允許批次檔中的其他命令,可以重覆地執行
for %%a IN (file1 file2 file3) DO del %%a
虛擬變數必須以兩個百分號(%%)起頭,in後面著參數列,do後面跟著要執行的命令
Ex1:
@echo off
echo .... 測試FOR指令
echo .... 請稍後
echo .... 完成.....
echo ....
for %%a IN (test_a.bat test_b.bat test_c.bat) do copy %%a prn
結果:
test Enter
.... 測試FOR指令
.... 請稍後
.... 完成.....
....
1 File(s) Copied
1 File(s) Copied
1 File(s) Copied
Ex2:
REM 取得日期
FOR /F "tokens=1-4 delims=/ " %%a IN ("%date%") DO (SET _MyDate=%%a%%b%%c%%d)
REM 顯示去掉分隔符號後的結果
echo %_MyDate%
說明:
/F 是指定將後面 %date% 的環境變數當成檔案來處理,而處理的依據就是在「"tokens=1-4 delims=/ "」這邊。tokens如果按照字面翻譯的話,是權杖的意思,這樣翻不太直覺,在這邊翻成順序可能比較好一些。以這個例子來說,就是要取得順序 1 ~ 4 的字串,那程式怎麼知道這個順序的分法呢?透過 delims 這個引述的設定,程式就知道要怎麼去分析 %date% 環境變數的順序。在 delims 等號右邊的字元就是用來指定分割順序的依據。以這個例子來說,我們用「/」與「 」(空白,White Space)這兩個分隔符號來分割 %date% 的順序。因此原本 %date% 的內容是 2006/03/07 星期二,在程式來看就變成了:
順序 內 容
﹉﹉ ﹉﹉﹉﹉
1 2006
2 03
3 07
4 星期二
%%a 是說把順序 1 的結果指派給名稱為 %%a 的變數,因此順序 2 的變數名稱就會是 %%b,以此類推,最多可以有 52 個變數,原因如下:變數名稱有大小寫之分,所以 %%a 跟 %%A 不同,且僅能以一個英文字母來命名,所以 26(個字母)× 2(大寫跟小寫 2 種)=52(個)。DO 後面的括號內容是用來指定所要執行的命令內容,以這個例子來說,就是指定一個名稱為 _MyDate 的變數,它的內容是由 %%a%%b%%c%%d 所構成的。
再來我們看看使用 FOR 指令去掉時間變數之分隔符號的結果:
MyTime.bat 的檔案內容如下所示:
----程式開始----
REM 取得時間
FOR /F "tokens=1-4 delims=:." %%a IN ("%time%") DO (SET _MyTime=%%a%%b%%c%%d)
REM 顯示去掉分隔符號後的結果
echo %_MyTime%
----程式結束----
看看執行的結果:
C:\>MyTime
螢幕輸出:
14243241
講了這麼多,您知道該怎麼運用 %date% 與 %time% 環境變數了嗎?
*------------------------------------------
* 八、 SHIFT
*------------------------------------------
SHIFT命令允許在批次檔的起動命令中,使用超過10以上的實際參數
(%0~%9)
*------------------------------------------
* 九、 CALL
*------------------------------------------
模組化的程式設計,是將一個完整的程式分割成一個個獨立的模組(module),每個模組負責一項功能
Ex:
@echo off
echo .... 測試CALL指令
echo .... 請稍後
echo %1 %2
call test_b
echo %3 %4
echo 程式模組已執行完畢 (test_a.bat)
@echo off
echo 執行test_b程式
echo 即將執行test_c .....
call test_c
echo 執行test_b程式完畢~~~ (test_b.bat)
@echo off
echo 執行test_c程式
if exist test2.bat type test2.bat
echo 執行test_c程式完畢~~~ (test_b.bat)
*------------------------------------------
* 十、 set
*------------------------------------------
宣告變數;設定變數
set gbs=%OS% 取得作業系統
set xdate=%date% 取得目前日期20080610
set xmonth=%date:~5,2% 取得目前月份06
set select=
set /P select=請選擇: 等待取得使用者所輸入字元
在DOS環境下輸入Set,可列出windows環境變數
2009年9月3日 星期四
[轉錄]C/C++之指標 (pointer),參考 (reference) 觀念整理與常見問題
from: Hung-Hsuan Chen (Sean)
文中某些小細節稍作修改
--------------------------------
前 言
這是以前替人代班教課時寫的一些東西
重新整理後放上來,一方面當作自己的備忘錄 (自己最看得懂的還是自己寫的東西)
另一方面如果有人有這方面的問題,希望此文能對你們也有一點點幫助。
--------------------------------
很多程式員說:學C/C++而不會使用指標,相當於沒學過C/C++。
本文針對C/C++中,指標與參考的常見問題或錯誤,做了一番整理,但求能達到拋磚引玉之效。如有疏漏或錯誤之處,尚請不吝告知指教。
目錄
- 何謂指標 (pointer)? 何謂參考 (reference)?
- call by value? call by address (或call by pointer)? call by reference? -- swap(int* a, int* b) v.s. swap (int &a, int &b)
- pointer to pointer, reference to pointer (int** v.s. int*&)
- function pointer
- void ** (*d) (int &, char **(*)(char *, char **))....如何看懂複雜的宣告…
我們先談指標 (pointer)。指標,其實也只是一個變數,只是這個變數的意義是:指向某個儲存位址。很玄嗎? 一點也不。下面這張圖就可以輕易的看出指標為何物。
圖 中,a, b, c, d, p1, p2都是一般的變數,儲存在記憶體 (memory) 中。其中,p1變數所記載的值是變數a的記憶體 (memory) 位址,而p2則記載著b的記憶體位址,像這樣的狀況,我們就稱p1是一個指向a的指標,相同的,p2是一個指向b的指標。
在C/C++中,我們用下面的式子來表示這個關係:
int *p1 = &a;
int *p2 = &b;
其中的&,稱為address of (取址)。即,p1 = address of a,p2 = address of b。
另一個符號*,代表的意義是指標。int *p1要由後往前閱讀來瞭解它的意義:p1 is a pointer points to an integer。因此,int *p1 = &a;這整行,我們可以看成:p1 is a pointer points to integer variable a,即:p1是一個指標,指向整數變數a。
且讓我們暫時打住指標的討論,轉頭看看參考 (reference)。
參考,可以想像成是一個變數或物件的別名 (alias)。通常,當函式 (function) 的參數 (parameter) 在函式中會被修改,而且要把這個修改結果讓呼叫函式的部份繼續使用,我們會用參考來當參數傳入函式中。
讓我們看看下面的例子:
void swap(int &a, int &b){
int tmp = a;
a = b;
b = tmp;
}
當其他程式呼叫此交換程式時,只要直接寫swap(x, y)就能交換x與y的值。在這裡,a和b為x與y的別名,即:a就是x,b就是y,如同美國國父就是華盛頓一樣。a和b不是x和y的複製品,任何做用在a與b上的動作都會反應在x與y上面,反之亦然。
指標和參考之所以難懂,有很大一部份的原因是符號上的陌生所致。加上&既能用於取址又能用於參考,容易造成初學者的混淆。下面我們提供幾個建議來幫助各位看懂這些符號。
- 把int *p視為 int* p。
把int和*連在一起看,當作是一種型態叫做 "指向整數之指標",要比int *p自然得多。同樣的方式也可以套在char* p或void* p等。但要注意的是下面的狀況: int* p, q;
不要把這行誤解成p, q都是指向int之指標,事實上,q只是一個int變數。上面這行相當於 int *p, q; 或 int *p; int q;
如果p, q都要宣告成指向int之指標,應寫成: int *p, *q
或者干脆分兩行寫: int* p;
int* q;
- 若&前面有資料型態 (ex: int &),則為參考,&前面有等號 (ex: int* p = &a),則為取址。
由於&同時具有多種意義,因此容易造成混淆。這裡列出的這個方法,可以幫助弄清楚每個&的意義。
JAVA中的reference與C++的reference意義上並不相同,卻使用同一個字,這也是reference容易造成混淆的原因。在此,我們暫不考慮JAVA中reference的觀念 (關於java中reference的觀念,請參考Reference in JAVA -- 淺談java的指標),純粹把主題放在C/C++上。
呼叫副函式時,call by value, address, 或reference是三種不同的參數傳遞方式。其意義如下:
- call by value
假設函式A呼叫函式B(p, q),則B中的p和q是「複製」自函式A所傳入的參數,B中對p, q所做的任何運算都不會影響到A中的p和q,因為B執行完後,並不會把複製的p, q存回到A中。這種參數傳遞方式,我們稱之為call by value。
以swap這個常見的函式為例,若swap寫成下面的樣子:
void swap(int a, int b){
int tmp = a;
a = b;
b = tmp;
}
則呼叫swap(x, y)後,x和y的值並不會有變化。 - call by address (或call by pointer)
利用指標來做參數傳遞,這種方法骨子裡仍是call by value,只不過call by value的value,其資料型態為指標罷了。我們同樣看看用call by address來寫swap交換兩個integer的例子。
void swap(int* a, int* b){
int tmp = *a;
*a = *b;
*b = tmp;
}
呼 叫swap時,要寫成swap(&x, &y)。呼叫swap時,x的指標 (x的儲存位置) 與y的指標 (y的儲存位置) 會被複製一份到swap中,然後把該位置內所記載的值做更換。swap結束後,&x (address of x) 和&y (address of y) 依然沒變,只是address of x所記錄之變數值與address of y所記錄之變數值交換了。因為&x 和&y 其實是利用call by value在傳,因此,call by address其實骨子裡就是call by value。 - call by reference
這是C++才加進來的東西,C本身並沒有call by reference。call by reference基本上是把參數做個別名 (alias),以下面的swap為例: swap(int &a, int &b){
int tmp = a;
a = b;
b = tmp;
}
未來使用時,只要呼叫swap(x, y),就可以讓x和y的值交換。在這個例子中,a 就是 x, b 就是 y。這個觀念在上一節已經提過,在此不再贅述。
當 我們用call by pointer (或address) 來傳遞參數時,被呼叫的函式複製一份pointer的值過去。但是,當我們想在函式內改變pointer的值 (而非pointer所指向之變數的值),而且改變的效果要能在函式外看得到時,call by pointer就不足夠用了。此時應該用的是"call by pointer to pointer"或"call by reference to pointer"。我們先看下面的例子:
int g_int = 0;
void changePtr(int* pInt){
pInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(localPInt);
printf("%d\n", *localPInt);
}
在這個例子中,印出來的數字仍然會是localInt的1,因為changPtr中的pInt是由localPInt「複製」過去的,對pInt做改變並不會反應到localPInt身上。
我們先用pointer to pointer對localPInt做改變,請看下例。
int g_int = 0;
void changePtr(int** pInt){
*pInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(&localPInt);
printf("%d\n", *localPInt);
}
本 例中,印出來的數字會是g_int的0。changePtr函式中的pInt是由&localPInt複製所得,因此對pInt做改變並不會影響 main中的&localPInt (資料型態:pointer to pointer to integer)。但在changePtr函式中我們改變的對象是pInt所指向的內容,因此這項改變在main中會顯示出來。
同樣的功能,我們也可改用reference to pointer來完成。但同樣切記,reference是C++才有的功能,因此reference to pointer也只能在支援C++的環境中使用。
int g_int = 0;
void changePtr(int* &refPInt){
refPInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(localPInt);
printf("%d\n", *localPInt);
}
這 段程式印出來的數字會是0。因為在changePtr中,我們宣告的參數型態為int* &,即:reference to pointer to integer。因此,main中的localPInt與changePtr函式中的refPInt其實是「同一件東西」。
另一種 常見的混淆是pointer array (指標陣列) 與pointer to pointers,因為兩種都可以寫成**的型式。如,int**可能是pointer to pointer to integer,也可能是integer pointer array。但pointer array的觀念相對來講要簡單且直觀許多,這裡我們就暫不花篇幅敘述。常見的例子:main(int argc, char** argv)其實應該是main(int argc, char* argv[])。
4. function pointer
變數的指標指向變數的位址,同樣的,function pointer (函式指標) 也是指向函式的位址的指標。
函式指標的加入,讓C/C++的符號更複雜,也使更多人望之而卻步。在說明函式指標的用途前,我們先直接由語法來看看函式指標該怎麼宣告、怎麼理解。
假設有個函式長成下面的樣子:
void func1(int int1, char char1);
我們想宣告一個能指向func1的指標,則寫成下面這樣:
void (*funcPtr1)(int, char);
這樣的寫法應理解成:funcPtr1是一個函數指標,它指向的函數接受int與char兩個參數並回傳void。如果今天有另一個函式長成
void func2(int int2, char char2);
則funcPtr1也能指向func2。
指標指向的方法,寫成下面這樣:
funcPtr1 = &func1;
取址符號省略亦可,效果相同:
funcPtr1 = func1;
若欲在宣告時就直接給予初值,則寫成下面這樣:
void (*funcPtr1)(int, char) = &func1; //&亦可省略
stdlib.h中提供的qsort函式是函式指標最常見的應用之一。此函式之prototype長得如下:
void qsort(void* base, size_t n, size_t size, int (*cmp)(const void*, const void*));
其中的int (*cmp)(const void*, const void*) 就使用到函式指標。
函式指標常見的使用時機是multithread時。函數指標負責把函數傳進建立執行緒的API中。
另外,callback function也是常使用函式指標的地方。所謂callback function即:發生某事件時,自動執行某些動作。在event driven的環境中,便時常使用callback function來實現此機制。
事實上,函式指標還能讓C語言實作polymorphism。但礙於篇幅,在此不再詳述。
5. void ** (*d) (int &, char **(*)(char *, char **))....如何看懂複雜的宣告…
在這裡,我們介紹兩種方式來看懂複雜的宣告。第一種要判斷的是:常數與指標混合使用時,到底const修飾的是指標還是指標所指的變數? 第二種是面對如標題所示這種複雜的宣告時,我們要怎麼讀懂它。
5.1 常數與指標的讀法
const double *ptr;
double *const ptr;
double const* ptr;
const double *const ptr;
以上幾個宣告,到底const修飾的對象是指標,還是指標所指向的變數呢?
其實,關鍵在於:*與const的前後關係!
當*在const之前,則是常數指標,反之則為常數變數。因此,
const double *ptr; // ptr指向常數變數
double *const ptr; // ptr是常數指標
double const* ptr; // ptr指向常數變數
const double *const ptr; // 指向常數變數的常數指標
事實上,在The C++ Programming Language中有提到一個簡單的要訣:由右向左讀!!讓我們用這個要訣再來試一次。
const double *ptr; // ptr is a pointer points to double, which is a constant
double *const ptr; // ptr is a constant pointer points to double
double const* ptr; // ptr is a pointer points to constant double
const double *const ptr; // ptr is a constant pointer points to double, which is a constant
結果完全相同 :-)
5.2 複雜宣告的讀法 void ** (*d) (int &, char **(*)(char *, char **)).......
其實閱讀C/C++中複雜的宣告有點像是讀英文的長句子,看多了,自然知道句子是怎麼構造出來的。
但對於句子還不熟的人,難免得藉助文法來拆解一個句子。關於C語言複雜宣告的解析文法,最令我印象深刻的,莫過於印度工程師Vikram的"The right-left rule"。他是這麼說的:
「從最內層的括號讀起,變數名稱,然後往右,遇到括號就往左。當括號內的東西都解讀完畢了,就跳出括號繼續未完成的部份,重覆上面的步驟直到解讀完畢。」
舉個例子:void ** (*d) (int &, char*)依下面方式解讀:
1. 最內層括號的讀起,變數名稱: d
2. 往右直到碰到) : (空白)
3. 往左直到碰到( :是一個函數指標
4. 跳出括號,往右,碰到(int &, char*): 此函式接受兩個參數:第一個參數是reference to integer,第二個參數是character pointer。
5. 往左遇上void **: 此函式回傳的型態為pointer to pointer to void。
==> d是一個函式指標,指向的函式接受int&和char*兩個參數並回傳void**的型態。
如何,是不是好懂很多了呢?
標題中的void ** (*d) (int &, char **(*)(char *, char **))其實和上面的例子幾乎一樣,只是函式的第二個參數又是一個函式指標,接受char*和char**兩個參數並回傳char**的型態。
行文至此,把指標和參考的常見問題與混淆大致地提了一些。希望能讓使用C/C++的人在面對或使用指標、參考、或取址時,不再有疙瘩在心中。文中若有不足或錯誤之處,也請高手不吝指教囉。:-)
C++ Learning
C++ Learning
C 語言常見問題集 http://twpug.net/docs/ccfaq/ccfaq.html
侯捷網站 - 出了非常多有關C/Window/Java書籍的老師,網站內容有許多書籍的勘誤與問答,內容非 常扎實,還有電子書可以下載。
CSDN -大陸最大的IT技術網站,寫程式必去的地方。
C Programming and C++ Programming - C/C++ very Good English Site.
C++ Language Tutorial -C++教學網站 還滿不錯的 資料滿齊全的 但是是英文網站,必須要有點耐心。
C++學習筆記-C++教學網站 資料範例也都很齊全,講解也蠻清楚的,重點他是中文滴,適合初學者。
VC++知識庫-裡面有許多好的文章以及範例,雖然叫做VC++知識庫但是內容包羅萬象,是個不錯滴地方,我也常到那邊看文章,是簡體網站。有些文章是翻譯英文網站滴,雖然不是原作,但是至少有翻譯的文章對應,台灣哪時候才能有這樣的網站呢?
Codeguru - 與Code porject網站差不多,也是值得一去的好地方。
CodeProject - Source Code相當豐富的網站,內容相當豐富,不侷限於一種程式語言,找Code的好地方。
移動技術網 - 相關嵌入式系統的網站,資料相當豐富的大陸網站。
Visual C++ Tutorial - Widow SDK & MFC,不錯的英文網站。
DevX - 不錯的C/C++學習網站。
乘風原創程序- BCB 相關文章,還有一些BCB控制硬體相關文章。大陸網站。
第二學堂- BCB相關基本教學網站。
香港學網 - C ,8051,Perl,JavaScript,Oracle..等程式語言基本教學。
Java examples - Java、C/C++、C#、PHP、Python、VB.Net、SQL相關教學文章。
International ACM Programming Content
OpenCV中文教程 - 影像處理的函式庫。
C Language I/O
int fprintf(FILE *stream, const char *format, ...)
可以看到,它與printf()函數相比多出來了第一個參數FILE *stream,其意義是將打印的內容輸出到文件流指針stream指向的流。所謂流,通常是指程序輸入或輸出的一個連續的字節序列,設備(例如鼠標、鍵盤、磁盤、屏幕、調製解調器和打印機)的輸入和輸出都是用流來處理的,在C語言中,所有的流均以文件的形式出現——不一定是物理磁盤文件,還可以是對應於某個輸入/輸出源的邏輯文件。 C語言提供了5種標準的流,你的程序在任何時候都可以使用它們,並且不必打開或關閉它們。以下列出了這5種標準的流。
名稱 描述 例子
stdin 標準輸入 鍵盤
其中,stdprn和stdaux並不總是預先定義好的,因為LPT1和COM1端口在某些操作系統中是沒有意義的,而stdin,stdout和stderr總是預先定義好的。此外,stdin並不一定來自鍵盤,stdout也並不一定顯示在屏幕上,它們都可以重定向到磁盤文件或其它設備上。我們在頭文件stdio.h中可以找到stdin,stdout和stderr的定義如下:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
在使用fprintf()函數時,通常我們可以將第一個參數設為stdout或者stderr,打印出錯調試信息的時候則推薦使用stderr而不是stdout,這是一種慣例,同時也由於內核在處理stdout和stderr時的優先級不一樣,後者的優先級要高一些,因此有時候如果程序異常退出時,stderr能得到輸出,而stdout就不行。
printf(...)實際上相當於fprintf(stdout, ...),這也是為什麼我們不推薦使用它的原因。在輸出調試信息的時候,我們推薦使用fprintf(stderr, …),或者使用某個指定的文件流fprintf(some_stream, …)。
那麼具體如何在必要的時候重定向fprintf()中的調試信息呢?來看看下面的一些方法:
當調試信息的量比較大,需要一些時間或者其他輔助工具來搜索過濾時,僅僅利用顯示屏幕來輸出調試信息是不夠的,這時我們經常將這些信息輸出到所謂的日誌文件(log)中,之後再仔細的分析log文件來發現問題。
利用Shell的I/O重定向
簡單的寫log方法可以通過shell的I/O重定向機制來實現,比如下面的代碼:
1 #include
2
3 int main()
4 {
5 fprintf(stdout, "This is a standard output info!\n");
6 fprintf(stderr, "This is a standard error output info!\n");
7 return 0;
8 }
在默認條件下,編譯運行的結果是打印信息都輸出在屏幕上:
$ gcc fprint.c -o fprint
$ ./fprint
This is a standard output info!
This is a standard error output info!
這是因為默認情況下,shell所打開的stdout和stderr設備都是顯示屏幕。不過我們可以通過shell的重定向功能來將打印信息寫到文件中去。比如:
$ ./fprint >output.log
This is a standard error output info!
$ cat output.log
This is a standard output info!
這樣,我們把stdout的輸出寫到了文件output.log中,不過stderr的輸出還是在屏幕上。如何重定向stderr呢?這需要用到shell定義的文件描述符。在shell下stdin, stdout,和stderr的文件描述符分別是0, 1和2,我們可以用下面的方法重定向:
$ cat output.log
This is a standard output info!
$ cat error.log
This is a standard error output info!
$
$ ./fprint >output.log 2>&1
$ cat output.log
This is a standard error output info!
This is a standard output info!
其中./fprint >output.log 2>error.log分別將stdout和stderr的輸出寫入到文件output.log和error.log中,而./fprint >output.log 2>&1則表示將stderr的輸出追加到stdout的文件output.log中(結果是output.log中既有stdout輸出也有stderr輸出)。
一些常用的shell I/O語法如下:
cmd > file把stdout重定向到file文件中
cmd >> file把stdout重定向到file文件中(追加)
cmd 1> fiel把stdout重定向到file文件中
cmd > file 2>&1把stdout和stderr一起重定向到file文件中
cmd 2> file把stderr重定向到file文件中
cmd 2>> file把stderr重定向到file文件中(追加)
cmd >> file 2>&1把stderr和stderr一起重定向到file文件中(追加)
在平時的簡單調試中,我們可以靈活利用這些方法來快速得到log文件。
用freopen()進行重定向
有時候我們要求在程序中能夠控制標準流的重定向,這時可以利用標準C庫函數freopen()。 freopen()的函數原型如下:
FILE *freopen(const char *filename, const char *mode, FILE *stream)
下面的代碼用來測試用函數freopen()重定向stderr:
1 #include
2
3 int main()
4 {
5 if (freopen("err.log", "w", stderr)==NULL)
6 fprintf(stderr, "error redirecting stderr\n");
7 fprintf(stdout, "This is a standard output info!\n");
8 fprintf(stderr, "This is a standard error output info!\n");
9 fclose(stderr);
10 return 0;
11 }
在第5行我們用freopen()函數將stderr重定向到了”err.log”文件,這樣得到的結果如下:
$ gcc print_log.c -o print_log
$ ./print_log
This is a standard output info!
$ cat err.log
This is a standard error output info!
可見第8行打印到stderr的信息被重定向到了err.log文件中,而第7行stdout的打印信息則還是輸出到了屏幕上。
printf( ) 的列印格式、控制字元、修飾子
列印格式- | -輸出敘述 |
%c | -字元 |
%s | -字串 |
%d | -十進位整數 |
%u | -無號十進位整數 |
%o | -無號八進位整數 |
%x | -無號十六進位整數,以 0 ~ f 表示 |
%X | -無號十六進位整數,以 0 ~ F 表示-- |
%f | -浮點數,小數點型式 |
%e | -浮點數,指數e型式 |
%E | -浮點數,指數E型式 |
%g | -印出 %f 與 %e 較短者 |
%G | -印出 %F 與 %E 較短者 |
%p | -指標位址 |
%% | -印出百分比符號 |
-控制字元-- | -功能 |
\a | -警告音 |
\b | -倒退 |
\f | -換頁 |
\n | -換行 |
\r | -歸位 |
\t | -跳格 |
\’ | -印出單引號 |
\” | -印出雙引號 |
\\ | -反斜線 |
\/ | -斜線 |
\d | -八進位 Ascii 碼 |
\x | -十六進位 Ascii 碼-- |
-修飾子- | -功能 | -範例 |
- | -向左對齊 | -%-3d |
+ | -將數值的正負號顯示出來 | -%+5d |
空白 | -數值為正值時,留一格空白;為負值時,顯示負號 | -% 6f |
0 | -將固定欄位長度的數值前空白處填上 0;與 – 修飾子同時使用時,此修飾子無效-- | -%07.2f-- |
數字 | -欄位長度,當數值的位數大於所定的欄位長度時,欄位會自動加寬它的長度 | -%9d |
. | -數值以 %e, %E, %f 型式表示時,決定小數點後所要顯示的位數 | -%4.3f |
h | -表示 short int 或是 unsigned short int | -%5h |
l | -表示 long int 或是 unsigned long int | -%lu |