網頁

2009年9月25日 星期五

Mail Server

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同時連線到一個郵箱。
* 支援在伺服器保留訊息狀態資訊。
* 支援在伺服器上存取多個郵箱。

推薦網誌

http://blog.leader.edu.tw/plog/index2.php?blogId=15

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)


這篇文章是由我舊的blog轉貼過來的


文中某些小細節稍作修改
--------------------------------
前 言
這是以前替人代班教課時寫的一些東西
重新整理後放上來,一方面當作自己的備忘錄 (自己最看得懂的還是自己寫的東西)
另一方面如果有人有這方面的問題,希望此文能對你們也有一點點幫助。
--------------------------------
很多程式員說:學C/C++而不會使用指標,相當於沒學過C/C++。
本文針對C/C++中,指標與參考的常見問題或錯誤,做了一番整理,但求能達到拋磚引玉之效。如有疏漏或錯誤之處,尚請不吝告知指教。


目錄
  1. 何謂指標 (pointer)? 何謂參考 (reference)?
  2. call by value? call by address (或call by pointer)? call by reference? -- swap(int* a, int* b) v.s. swap (int &a, int &b)
  3. pointer to pointer, reference to pointer (int** v.s. int*&)
  4. function pointer
  5. void ** (*d) (int &, char **(*)(char *, char **))....如何看懂複雜的宣告…
1. 何謂指標 (pointer)? 何謂參考 (reference)?
我們先談指標 (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),則為取址
    由於&同時具有多種意義,因此容易造成混淆。這裡列出的這個方法,可以幫助弄清楚每個&的意義。
2. call by value? call by address (或call by pointer)? call by reference? -- swap(int* a, int* b) v.s. swap (int &a, int &b)
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。這個觀念在上一節已經提過,在此不再贅述。
3. pointer to pointer, reference to pointer (int** v.s. int*&)
當 我們用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的好地方。

工程師的家

移動技術網 - 相關嵌入式系統的網站,資料相當豐富的大陸網站。

WinCE MS官方網站

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相關教學文章。

C++ 程式討論版(內含教學文件)

C程式簡介

cs9-C++ 部落格文章

NTOU CS 程式設計課程

駱思安老師的教學網站

C++ FAQ Lite

International ACM Programming Content

Lucky 貓的 ACM 園地

部落格文章(Embeded linux 教學)

OpenCV中文教程 - 影像處理的函式庫。

C++ Standards Committee Papers

Just software solutions-Blog Archive for / cplusplus /

C Language I/O

http://www.eefocus.com/html/09-05/5311327060403cJqm.shtml



除了人工的分析之外,最簡單最直接的調試方法要算printf了。不過,我們這裡推薦使用的並不是初學C語言時使用的函數int printf(const char *format, ...),而是稍微複雜一點的fprintf()函數,因為它更方便我們之後重定向錯誤輸出信息到指定的設備。 fprintf()函數的原型如下:
int fprintf(FILE *stream, const char *format, ...)

可以看到,它與printf()函數相比多出來了第一個參數FILE *stream,其意義是將打印的內容輸出到文件流指針stream指向的流。所謂流,通常是指程序輸入或輸出的一個連續的字節序列,設備(例如鼠標、鍵盤、磁盤、屏幕、調製解調器和打印機)的輸入和輸出都是用流來處理的,在C語言中,所有的流均以文件的形式出現——不一定是物理磁盤文件,還可以是對應於某個輸入/輸出源的邏輯文件。 C語言提供了5種標準的流,你的程序在任何時候都可以使用它們,並且不必打開或關閉它們。以下列出了這5種標準的流。
----------------------------------------
名稱 描述 例子
stdin 標準輸入 鍵盤
stdout 標準輸出 屏幕
stderr 標準錯誤 屏幕
stdprn 標準打印機 LPT1端口
stdaux 標準串行設備 COM1端口
----------------------------------------



其中,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,我們可以用下面的方法重定向:

$ ./fprint >output.log 2>error.log
$ 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