linux 操做系統下c語言編程入門

2)Linux程序設計入門--進程介紹 
3)Linux程序設計入門--文件操做 
4)Linux程序設計入門--時間概念 
5)Linux程序設計入門--信號處理 
6)Linux程序設計入門--消息管理 
7)Linux程序設計入門--線程操做 
8)Linux程序設計入門--網絡編程 
9)Linux下C開發工具介紹
php

 

1)Linux程序設計入門--基礎知識 
Linux下C語言編程基礎知識 
前言: 
這篇文章介紹在LINUX下進行C語言編程所須要的基礎知識.在這篇文章當中,咱們將 
會學到如下內容: 
源程序編譯 
Makefile的編寫 
程序庫的連接 
程序的調試 
頭文件和系統求助 
---------------------------------------------------------------------------- 
---- 
1.源程序的編譯 
在Linux下面,若是要編譯一個C語言源程序,咱們要使用GNU的gcc編譯器. 下面咱們 
以一個實例來講明如何使用gcc編譯器. 
假設咱們有下面一個很是簡單的源程序(hello.c): 
int main(int argc,char **argv) 

printf("Hello Linux/n"); 

要編譯這個程序,咱們只要在命令行下執行: 
gcc -o hello hello.c 
gcc 編譯器就會爲咱們生成一個hello的可執行文件.執行./hello就能夠看到程序的輸出 
結果了.命令行中 gcc表示咱們是用gcc來編譯咱們的源程序,-o 選項表示咱們要求編譯 
器給咱們輸出的可執行文件名爲hello 而hello.c是咱們的源程序文件. 
gcc編譯器有許多選項,通常來講咱們只要知道其中的幾個就夠了. -o選項咱們已經知道 
了,表示咱們要求輸出的可執行文件名. -c選項表示咱們只要求編譯器輸出目標代碼,而 
沒必要要輸出可執行文件. -g選項表示咱們要求編譯器在編譯的時候提供咱們之後對程序 
進行調試的信息. 
知道了這三個選項,咱們就能夠編譯咱們本身所寫的簡單的源程序了,若是你想要知道更 
多的選項,能夠查看gcc的幫助文檔,那裏有着許多對其它選項的詳細說明. 
2.Makefile的編寫 
假設咱們有下面這樣的一個程序,源代碼以下: 
/* main.c */ 
#include "mytool1.h" 
#include "mytool2.h" 
int main(int argc,char **argv) 

mytool1_print("hello"); 
mytool2_print("hello"); 

/* mytool1.h */ 
#ifndef _MYTOOL_1_H 
#define _MYTOOL_1_H 
void mytool1_print(char *print_str); 
#endif 
/* mytool1.c */ 
#include "mytool1.h" 
void mytool1_print(char *print_str) 

printf("This is mytool1 print %s/n",print_str); 

/* mytool2.h */ 
#ifndef _MYTOOL_2_H 
#define _MYTOOL_2_H 
void mytool2_print(char *print_str); 
#endif 
/* mytool2.c */ 
#include "mytool2.h" 
void mytool2_print(char *print_str) 

printf("This is mytool2 print %s/n",print_str); 

固然因爲這個程序是很短的咱們能夠這樣來編譯 
gcc -c main.c 
gcc -c mytool1.c 
gcc -c mytool2.c 
gcc -o main main.o mytool1.o mytool2.o 
這樣的話咱們也能夠產生main程序,並且也不時很麻煩.可是若是咱們考慮一下若是有一 
天咱們修改了其中的一個文件(好比說mytool1.c)那麼咱們難道還要從新輸入上面的命令 
?也許你會說,這個很容易解決啊,我寫一個SHELL腳本,讓她幫我去完成不就能夠了.是的 
對於這個程序來講,是能夠起到做用的.可是當咱們把事情想的更復雜一點,若是咱們的程 
序有幾百個源程序的時候,難道也要編譯器從新一個一個的去編譯? 
爲此,聰明的程序員們想出了一個很好的工具來作這件事情,這就是make.咱們只要執行以 
下make,就能夠把上面的問題解決掉.在咱們執行make以前,咱們要先編寫一個很是重要的 
文件.--Makefile.對於上面的那個程序來講,可能的一個Makefile的文件是: 
# 這是上面那個程序的Makefile文件 
main:main.o mytool1.o mytool2.o 
gcc -o main main.o mytool1.o mytool2.o 
main.o:main.c mytool1.h mytool2.h 
gcc -c main.c 
mytool1.o:mytool1.c mytool1.h 
gcc -c mytool1.c 
mytool2.o:mytool2.c mytool2.h 
gcc -c mytool2.c 
有了這個Makefile文件,不過咱們何時修改了源程序當中的什麼文件,咱們只要執行 
make命令,咱們的編譯器都只會去編譯和咱們修改的文件有關的文件,其它的文件她連理 
都不想去理的. 
下面咱們學習Makefile是如何編寫的. 
在Makefile中也#開始的行都是註釋行.Makefile中最重要的是描述文件的依賴關係的說 
明.通常的格式是: 
target: components 
TAB rule 
第一行表示的是依賴關係.第二行是規則. 
好比說咱們上面的那個Makefile文件的第二行 
main:main.o mytool1.o mytool2.o 
表示咱們的目標(target)main的依賴對象(components)是main.o mytool1.o mytool2.o 
當倚賴的對象在目標修改後修改的話,就要去執行規則一行所指定的命令.就象咱們的上 
面那個Makefile第三行所說的同樣要執行 gcc -o main main.o mytool1.o mytool2.o 
注意規則一行中的TAB表示那裏是一個TAB鍵 
Makefile有三個很是有用的變量.分別是$@,$^,$<表明的意義分別是: 
$@--目標文件,$^--全部的依賴文件,$<--第一個依賴文件. 
若是咱們使用上面三個變量,那麼咱們能夠簡化咱們的Makefile文件爲: 
# 這是簡化後的Makefile 
main:main.o mytool1.o mytool2.o 
gcc -o $@ $^ 
main.o:main.c mytool1.h mytool2.h 
gcc -c $< 
mytool1.o:mytool1.c mytool1.h 
gcc -c $< 
mytool2.o:mytool2.c mytool2.h 
gcc -c $< 
通過簡化後咱們的Makefile是簡單了一點,不過人們有時候還想簡單一點.這裏咱們學習 
一個Makefile的缺省規則 
..c.o: 
gcc -c $< 
這個規則表示全部的 .o文件都是依賴與相應的.c文件的.例如mytool.o依賴於mytool.c 
這樣Makefile還能夠變爲: 
# 這是再一次簡化後的Makefile 
main:main.o mytool1.o mytool2.o 
gcc -o $@ $^ 
..c.o: 
gcc -c $< 
好了,咱們的Makefile 也差很少了,若是想知道更多的關於Makefile規則能夠查看相應的 
文檔. 
3.程序庫的連接 
試着編譯下面這個程序 
/* temp.c */ 
#include <math.h> 
int main(int argc,char **argv) 

double value; 
printf("Value:%f/n",value); 

這個程序至關簡單,可是當咱們用 gcc -o temp temp.c 編譯時會出現下面所示的錯誤. 

/tmp/cc33Kydu.o: In function `main': 
/tmp/cc33Kydu.o(.text+0xe): undefined reference to `log' 
collect2: ld returned 1 exit status 
出現這個錯誤是由於編譯器找不到log的具體實現.雖然咱們包括了正確的頭文件,可是我 
們在編譯的時候仍是要鏈接肯定的庫.在Linux下,爲了使用數學函數,咱們必須和數學庫 
鏈接,爲此咱們要加入 -lm 選項. gcc -o temp temp.c -lm這樣纔可以正確的編譯.也許 
有人要問,前面咱們用printf函數的時候怎麼沒有鏈接庫呢?是這樣的,對於一些經常使用的函 
數的實現,gcc編譯器會自動去鏈接一些經常使用庫,這樣咱們就沒有必要本身去指定了. 有時 
候咱們在編譯程序的時候還要指定庫的路徑,這個時候咱們要用到編譯器的 -L選項指定 
路徑.好比說咱們有一個庫在 /home/hoyt/mylib下,這樣咱們編譯的時候還要加上 -L/h 
ome/hoyt/mylib.對於一些標準庫來講,咱們沒有必要指出路徑.只要它們在起缺省庫的路 
徑下就能夠了.系統的缺省庫的路徑/lib /usr/lib /usr/local/lib 在這三個路徑下面 
的庫,咱們能夠不指定路徑. 
還有一個問題,有時候咱們使用了某個函數,可是咱們不知道庫的名字,這個時候怎麼辦呢 
?很抱歉,對於這個問題我也不知道答案,我只有一個傻辦法.首先,我到標準庫路徑下面去 
找看看有沒有和我用的函數相關的庫,我就這樣找到了線程(thread)函數的庫文件(libp 
thread.a). 固然,若是找不到,只有一個笨方法.好比我要找sin這個函數所在的庫. 就只 
好用 nm -o /lib/*.so|grep sin>~/sin 命令,而後看~/sin文件,到那裏面去找了. 在s 
in文件當中,我會找到這樣的一行libm-2.1.2.so:00009fa0 W sin 這樣我就知道了sin在 
libm-2.1.2.so庫裏面,我用 -lm選項就能夠了(去掉前面的lib和後面的版本標誌,就剩 
下m了因此是 -lm). 若是你知道怎麼找,請趕快告訴我,我回很是感激的.謝謝! 
4.程序的調試 
咱們編寫的程序不太可能一次性就會成功的,在咱們的程序當中,會出現許許多多我 
們想不到的錯誤,這個時候咱們就要對咱們的程序進行調試了. 
最經常使用的調試軟件是gdb.若是你想在圖形界面下調試程序,那麼你如今能夠選擇xxgdb.記 
得要在編譯的時候加入 -g選項.關於gdb的使用能夠看gdb的幫助文件.因爲我沒有用過這 
個軟件,因此我也不可以說出如何使用. 不過我不喜歡用gdb.跟蹤一個程序是很煩的事情 
,我通常用在程序當中輸出中間變量的值來調試程序的.固然你能夠選擇本身的辦法,沒有 
必要去學別人的.如今有了許多IDE環境,裏面已經本身帶了調試器了.你能夠選擇幾個試 
一試找出本身喜歡的一個用. 
5.頭文件和系統求助 
有時候咱們只知道一個函數的大概形式,不記得確切的表達式,或者是不記得着函數 
在那個頭文件進行了說明.這個時候咱們能夠求助系統. 
好比說咱們想知道fread這個函數的確切形式,咱們只要執行 man fread 系統就會輸出着 
函數的詳細解釋的.和這個函數所在的頭文件<stdio.h>說明了. 若是咱們要write這個函 
數的說明,當咱們執行man write時,輸出的結果卻不是咱們所須要的. 由於咱們要的是w 
rite這個函數的說明,但是出來的倒是write這個命令的說明.爲了獲得write的函數說明 
咱們要用 man 2 write. 2表示咱們用的write這個函數是系統調用函數,還有一個咱們常 
用的是3表示函數是C的庫函數. 
記住無論何時,man都是咱們的最好助手. 
------------------------------------------------------------------------ 
好了,這一章就講這麼多了,有了這些知識咱們就能夠進入激動人心的Linux下的C程序探 
險活動. 

2)Linux程序設計入門--進程介紹 
Linux下進程的建立 
前言: 
這篇文章是用來介紹在Linux下和進程相關的各個概念.咱們將會學到: 
進程的概念 
進程的身份 
進程的建立 
守護進程的建立 
---------------------------------------------------------------------------- 
---- 
1。進程的概念 
Linux操做系統是面向多用戶的.在同一時間能夠有許多用戶向操做系統發出各類命 
令.那麼操做系統是怎麼實現多用戶的環境呢? 在現代的操做系統裏面,都有程序和進程 
的概念.那麼什麼是程序,什麼是進程呢? 通俗的講程序是一個包含能夠執行代碼的文件 
,是一個靜態的文件.而進程是一個開始執行可是尚未結束的程序的實例.就是可執行文 
件的具體實現. 一個程序可能有許多進程,而每個進程又能夠有許多子進程.依次循環 
下去,而產生子孫進程. 當程序被系統調用到內存之後,系統會給程序分配必定的資源(內 
存,設備等等)而後進行一系列的複雜操做,使程序變成進程以供系統調用.在系統裏面只 
有進程沒有程序,爲了區分各個不一樣的進程,系統給每個進程分配了一個ID(就象咱們的 
身份證)以便識別. 爲了充分的利用資源,系統還對進程區分了不一樣的狀態.將進程分爲新 
建,運行,阻塞,就緒和完成五個狀態. 新建表示進程正在被建立,運行是進程正在運行,阻 
塞是進程正在等待某一個事件發生,就緒是表示系統正在等待CPU來執行命令,而完成表示 
進程已經結束了系統正在回收資源. 關於進程五個狀態的詳細解說咱們能夠看《操做系 
統》上面有詳細的解說。 
2。進程的標誌 
上面咱們知道了進程都有一個ID,那麼咱們怎麼獲得進程的ID呢?系統調用getpid可 
以獲得進程的ID,而getppid能夠獲得父進程(建立調用該函數進程的進程)的ID. 
#include <unistd> 
pid_t getpid(void); 
pid_t getppid(void); 
進程是爲程序服務的,而程序是爲了用戶服務的.系統爲了找到進程的用戶名,還爲進程和 
用戶創建聯繫.這個用戶稱爲進程的全部者.相應的每個用戶也有一個用戶ID.經過系統 
調用getuid能夠獲得進程的全部者的ID.因爲進程要用到一些資源,而Linux對系統資源是 
進行保護的,爲了獲取必定資源進程還有一個有效用戶ID.這個ID和系統的資源使用有關 
,涉及到進程的權限. 經過系統調用geteuid咱們能夠獲得進程的有效用戶ID. 和用戶ID 
相對應進程還有一個組ID和有效組ID系統調用getgid和getegid能夠分別獲得組ID和有效 
組ID 
#include <unistd> 
#include <sys/types.h> 

uid_t getuid(void); 
uid_t geteuid(void); 
gid_t getgid(void); 
git_t getegid(void); 
有時候咱們還會對用戶的其餘信息感興趣(登陸名等等),這個時候咱們能夠調用getpwui 
d來獲得. 
struct passwd { 
char *pw_name; /* 登陸名稱 */ 
char *pw_passwd; /* 登陸口令 */ 
uid_t pw_uid; /* 用戶ID */ 
gid_t pw_gid; /* 用戶組ID */ 
char *pw_gecos; /* 用戶的真名 */ 
char *pw_dir; /* 用戶的目錄 */ 
char *pw_shell; /* 用戶的SHELL */ 
}; 
#include <pwd.h> 
#include <sys/types.h> 

struct passwd *getpwuid(uid_t uid); 
下面咱們學習一個實例來實踐一下上面咱們所學習的幾個函數: 
#include <unistd.h> 
#include <pwd.h> 
#include <sys/types.h> 
#include <stdio.h> 
int main(int argc,char **argv) 

pid_t my_pid,parent_pid; 
uid_t my_uid,my_euid; 
gid_t my_gid,my_egid; 
struct passwd *my_info; 
my_pid=getpid(); 
parent_pid=getppid(); 
my_uid=getuid(); 
my_euid=geteuid(); 
my_gid=getgid(); 
my_egid=getegid(); 
my_info=getpwuid(my_uid); 
printf("Process ID:%ld/n",my_pid); 
printf("Parent ID:%ld/n",parent_pid); 
printf("User ID:%ld/n",my_uid); 
printf("Effective User ID:%ld/n",my_euid); 
printf("Group ID:%ld/n",my_gid); 
printf("Effective Group ID:%ld/n",my_egid): 
if(my_info) 

printf("My Login Name:%s/n" ,my_info->pw_name); 
printf("My Password :%s/n" ,my_info->pw_passwd); 
printf("My User ID :%ld/n",my_info->pw_uid); 
printf("My Group ID :%ld/n",my_info->pw_gid); 
printf("My Real Name:%s/n" ,my_info->pw_gecos); 
printf("My Home Dir :%s/n", my_info->pw_dir); 
printf("My Work Shell:%s/n", my_info->pw_shell); 


3。進程的建立 
建立一個進程的系統調用很簡單.咱們只要調用fork函數就能夠了. 
#include <unistd.h> 

pid_t fork(); 
當一個進程調用了fork之後,系統會建立一個子進程.這個子進程和父進程不一樣的地方只 
有他的進程ID和父進程ID,其餘的都是同樣.就象符進程克隆(clone)本身同樣.固然建立 
兩個如出一轍的進程是沒有意義的.爲了區分父進程和子進程,咱們必須跟蹤fork的返回 
值. 當fork掉用失敗的時候(內存不足或者是用戶的最大進程數已到)fork返回-1,不然f 
ork的返回值有重要的做用.對於父進程fork返回子進程的ID,而對於fork子進程返回0.我 
們就是根據這個返回值來區分父子進程的. 父進程爲何要建立子進程呢?前面咱們已經 
說過了Linux是一個多用戶操做系統,在同一時間會有許多的用戶在爭奪系統的資源.有時 
進程爲了早一點完成任務就建立子進程來爭奪資源. 一旦子進程被建立,父子進程一塊兒從 
fork處繼續執行,相互競爭系統的資源.有時候咱們但願子進程繼續執行,而父進程阻塞直 
到子進程完成任務.這個時候咱們能夠調用wait或者waitpid系統調用. 
#include <sys/types.h> 
#include <sys/wait.h> 

pid_t wait(int *stat_loc); 
pid_t waitpid(pid_t pid,int *stat_loc,int options); 
wait系統調用會使父進程阻塞直到一個子進程結束或者是父進程接受到了一個信號.若是 
沒有父進程沒有子進程或者他的子進程已經結束了wait回當即返回.成功時(因一個子進 
程結束)wait將返回子進程的ID,不然返回-1,並設置全局變量errno.stat_loc是子進程的 
退出狀態.子進程調用exit,_exit 或者是return來設置這個值. 爲了獲得這個值Linux定 
義了幾個宏來測試這個返回值. 
WIFEXITED:判斷子進程退出值是非0 
WEXITSTATUS:判斷子進程的退出值(當子進程退出時非0). 
WIFSIGNALED:子進程因爲有沒有得到的信號而退出. 
WTERMSIG:子進程沒有得到的信號號(在WIFSIGNALED爲真時纔有意義). 
waitpid等待指定的子進程直到子進程返回.若是pid爲正值則等待指定的進程(pid).若是 
爲0則等待任何一個組ID和調用者的組ID相同的進程.爲-1時等同於wait調用.小於-1時等 
待任何一個組ID等於pid絕對值的進程. stat_loc和wait的意義同樣. options能夠決定 
父進程的狀態.能夠取兩個值 WNOHANG:父進程當即返回當沒有子進程存在時. WUNTACHE 
D:當子進程結束時waitpid返回,可是子進程的退出狀態不可獲得. 
父進程建立子進程後,子進程通常要執行不一樣的程序.爲了調用系統程序,咱們能夠使用系 
統調用exec族調用.exec族調用有着5個函數. 
#include <unistd.h> 
int execl(const char *path,const char *arg,...); 
int execlp(const char *file,const char *arg,...); 
int execle(const char *path,const char *arg,...); 
int execv(const char *path,char *const argv[]); 
int execvp(const char *file,char *const argv[]): 
exec族調用能夠執行給定程序.關於exec族調用的詳細解說能夠參考系統手冊(man exec 
l). 下面咱們來學習一個實例.注意編譯的時候要加 -lm以便鏈接數學函數庫. 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <errno.h> 
#include <math.h> 
void main(void) 

pid_t child; 
int status; 
printf("This will demostrate how to get child status/n"); 
if((child=fork())==-1) 

printf("Fork Error :%s/n",strerror(errno)); 
exit(1); 

else if(child==0) 

int i; 
printf("I am the child:%ld/n",getpid()); 
for(i=0;i<1000000;i++) sin(i); 
i=5; 
printf("I exit with %d/n",i); 
exit(i); 

while(((child=wait(&status))==-1)&(errno==EINTR)); 
if(child==-1) 
printf("Wait Error:%s/n",strerror(errno)); 
else if(!status) 
printf("Child %ld terminated normally return status is zero/n", 
child); 
else if(WIFEXITED(status)) 
printf("Child %ld terminated normally return status is %d/n", 
child,WEXITSTATUS(status)); 
else if(WIFSIGNALED(status)) 
printf("Child %ld terminated due to signal %d znot caught/n", 
child,WTERMSIG(status)); 

strerror函數會返回一個指定的錯誤號的錯誤信息的字符串. 
4。守護進程的建立 
若是你在DOS時代編寫過程序,那麼你也許知道在DOS下爲了編寫一個常駐內存的程序 
咱們要編寫多少代碼了.相反若是在Linux下編寫一個"常駐內存"的程序倒是很容易的.我 
們只要幾行代碼就能夠作到. 實際上因爲Linux是多任務操做系統,咱們就是不編寫代碼 
也能夠把一個程序放到後臺去執行的.咱們只要在命令後面加上&符號SHELL就會把咱們的 
程序放到後臺去運行的. 這裏咱們"開發"一個後臺檢查郵件的程序.這個程序每一個一個指 
定的時間回去檢查咱們的郵箱,若是發現咱們有郵件了,會不斷的報警(經過機箱上的小喇 
叭來發出聲音). 後面有這個函數的增強版本增強版本 
後臺進程的建立思想: 首先父進程建立一個子進程.而後子進程殺死父進程(是否是很無 
情?). 信號處理全部的工做由子進程來處理. 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <signal.h> 
/* Linux 的默任我的的郵箱地址是 /var/spool/mail/用戶的登陸名 */ 
#define MAIL "/var/spool/mail/hoyt" 
/* 睡眠10秒鐘 */ 

#define SLEEP_TIME 10 
main(void) 

pid_t child; 
if((child=fork())==-1) 

printf("Fork Error:%s/n",strerror(errno)); 
exit(1); 

else if(child>0) 
while(1); 
if(kill(getppid(),SIGTERM)==-1) 

printf("Kill Parent Error:%s/n",strerror(errno)); 
exit(1); 


int mailfd; 
while(1) 

if((mailfd=open(MAIL,O_RDONLY))!=-1) 

fprintf(stderr,"%s","7"); 
close(mailfd); 

sleep(SLEEP_TIME); 



你能夠在默認的路徑下建立你的郵箱文件,而後測試一下這個程序.固然這個程序還有很 
多地方要改善的.咱們後面會對這個小程序改善的,再看個人改善以前你能夠嘗試本身改 
善一下.好比讓用戶指定郵相的路徑和睡眠時間等等.相信本身能夠作到的.動手吧,勇敢 
的探險者. 
好了進程一節的內容咱們就先學到這裏了.進程是一個很是重要的概念,許多的程序都會 
用子進程.建立一個子進程是每個程序員的基本要求! 

3)Linux程序設計入門--文件操做 
Linux下文件的操做 
前言: 
咱們在這一節將要討論linux下文件操做的各個函數. 
文件的建立和讀寫 
文件的各個屬性 
目錄文件的操做 
管道文件 
---------------------------------------------------------------------------- 
---- 
1。文件的建立和讀寫 
我假設你已經知道了標準級的文件操做的各個函數(fopen,fread,fwrite等等).固然 
若是你不清楚的話也不要着急.咱們討論的系統級的文件操做其實是爲標準級文件操做 
服務的. 
當咱們須要打開一個文件進行讀寫操做的時候,咱們能夠使用系統調用函數open.使用完 
成之後咱們調用另一個close函數進行關閉操做. 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 

int open(const char *pathname,int flags); 
int open(const char *pathname,int flags,mode_t mode); 
int close(int fd); 
open函數有兩個形式.其中pathname是咱們要打開的文件名(包含路徑名稱,缺省是認爲在 
當前路徑下面).flags能夠去下面的一個值或者是幾個值的組合. 
O_RDONLY:以只讀的方式打開文件. 
O_WRONLY:以只寫的方式打開文件. 
O_RDWR:以讀寫的方式打開文件. 
O_APPEND:以追加的方式打開文件. 
O_CREAT:建立一個文件. 
O_EXEC:若是使用了O_CREAT並且文件已經存在,就會發生一個錯誤. 
O_NOBLOCK:以非阻塞的方式打開一個文件. 
O_TRUNC:若是文件已經存在,則刪除文件的內容. 
前面三個標誌只能使用任意的一個.若是使用了O_CREATE標誌,那麼咱們要使用open的第 
二種形式.還要指定mode標誌,用來表示文件的訪問權限.mode能夠是如下狀況的組合. 
----------------------------------------------------------------- 
S_IRUSR 用戶能夠讀 S_IWUSR 用戶能夠寫 
S_IXUSR 用戶能夠執行 S_IRWXU 用戶能夠讀寫執行 
----------------------------------------------------------------- 
S_IRGRP 組能夠讀 S_IWGRP 組能夠寫 
S_IXGRP 組能夠執行 S_IRWXG 組能夠讀寫執行 
----------------------------------------------------------------- 
S_IROTH 其餘人能夠讀 S_IWOTH 其餘人能夠寫 
S_IXOTH 其餘人能夠執行 S_IRWXO 其餘人能夠讀寫執行 
----------------------------------------------------------------- 
S_ISUID 設置用戶執行ID S_ISGID 設置組的執行ID 
----------------------------------------------------------------- 
咱們也能夠用數字來表明各個位的標誌.Linux總共用5個數字來表示文件的各類權限. 
00000.第一位表示設置用戶ID.第二位表示設置組ID,第三位表示用戶本身的權限位,第四 
位表示組的權限,最後一位表示其餘人的權限. 
每一個數字能夠取1(執行權限),2(寫權限),4(讀權限),0(什麼也沒有)或者是這幾個值的和 
.. 
好比咱們要建立一個用戶讀寫執行,組沒有權限,其餘人讀執行的文件.設置用戶ID位那麼 
咱們能夠使用的模式是--1(設置用戶ID)0(組沒有設置)7(1+2+4)0(沒有權限,使用缺省) 
5(1+4)即10705: 
open("temp",O_CREAT,10705); 
若是咱們打開文件成功,open會返回一個文件描述符.咱們之後對文件的全部操做就能夠 
對這個文件描述符進行操做了. 
當咱們操做完成之後,咱們要關閉文件了,只要調用close就能夠了,其中fd是咱們要關閉 
的文件描述符. 
文件打開了之後,咱們就要對文件進行讀寫了.咱們能夠調用函數read和write進行文件的 
讀寫. 
#include <unistd.h> 
ssize_t read(int fd, void *buffer,size_t count); 
ssize_t write(int fd, const void *buffer,size_t count); 
fd是咱們要進行讀寫操做的文件描述符,buffer是咱們要寫入文件內容或讀出文件內容的 
內存地址.count是咱們要讀寫的字節數. 
對於普通的文件read從指定的文件(fd)中讀取count字節到buffer緩衝區中(記住咱們必 
須提供一個足夠大的緩衝區),同時返回count. 
若是read讀到了文件的結尾或者被一個信號所中斷,返回值會小於count.若是是由信號中 
斷引發返回,並且沒有返回數據,read會返回-1,且設置errno爲EINTR.當程序讀到了文件 
結尾的時候,read會返回0. 
write從buffer中寫count字節到文件fd中,成功時返回實際所寫的字節數. 
下面咱們學習一個實例,這個實例用來拷貝文件. 
#include <unistd.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <errno.h> 
#include <string.h> 
#define BUFFER_SIZE 1024 
int main(int argc,char **argv) 

int from_fd,to_fd; 
int bytes_read,bytes_write; 
char buffer[BUFFER_SIZE]; 
char *ptr; 
if(argc!=3) 

fprintf(stderr,"Usage:%s fromfile tofile/n/a",argv[0]); 
exit(1); 

/* 打開源文件 */ 
if((from_fd=open(argv[1],O_RDONLY))==-1) 

fprintf(stderr,"Open %s Error:%s/n",argv[1],strerror(errno)); 
exit(1); 

/* 建立目的文件 */ 
if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1) 

fprintf(stderr,"Open %s Error:%s/n",argv[2],strerror(errno)); 
exit(1); 

/* 如下代碼是一個經典的拷貝文件的代碼 */ 
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE)) 

/* 一個致命的錯誤發生了 */ 
if((bytes_read==-1)&&(errno!=EINTR)) break; 
else if(bytes_read>0) 

ptr=buffer; 
while(bytes_write=write(to_fd,ptr,bytes_read)) 

/* 一個致命錯誤發生了 */ 
if((bytes_write==-1)&&(errno!=EINTR))break; 
/* 寫完了全部讀的字節 */ 
else if(bytes_write==bytes_read) break; 
/* 只寫了一部分,繼續寫 */ 
else if(bytes_write>0) 

ptr+=bytes_write; 
bytes_read-=bytes_write; 


/* 寫的時候發生的致命錯誤 */ 
if(bytes_write==-1)break; 


close(from_fd); 
close(to_fd); 
exit(0); 

2。文件的各個屬性 
文件具備各類各樣的屬性,除了咱們上面所知道的文件權限之外,文件還有建立時間 
,大小等等屬性. 
有時侯咱們要判斷文件是否能夠進行某種操做(讀,寫等等).這個時候咱們能夠使用acce 
ss函數. 
#include <unistd.h> 

int access(const char *pathname,int mode); 
pathname:是文件名稱,mode是咱們要判斷的屬性.能夠取如下值或者是他們的組合. 
R_OK文件能夠讀,W_OK文件能夠寫,X_OK文件能夠執行,F_OK文件存在.當咱們測試成功時 
,函數返回0,不然若是有一個條件不符時,返回-1. 
若是咱們要得到文件的其餘屬性,咱們能夠使用函數stat或者fstat. 
#include <sys/stat.h> 
#include <unistd.h> 
int stat(const char *file_name,struct stat *buf); 
int fstat(int filedes,struct stat *buf); 
struct stat { 
dev_t st_dev; /* 設備 */ 
ino_t st_ino; /* 節點 */ 
mode_t st_mode; /* 模式 */ 
nlink_t st_nlink; /* 硬鏈接 */ 
uid_t st_uid; /* 用戶ID */ 
gid_t st_gid; /* 組ID */ 
dev_t st_rdev; /* 設備類型 */ 
off_t st_off; /* 文件字節數 */ 
unsigned long st_blksize; /* 塊大小 */ 
unsigned long st_blocks; /* 塊數 */ 
time_t st_atime; /* 最後一次訪問時間 */ 
time_t st_mtime; /* 最後一次修改時間 */ 
time_t st_ctime; /* 最後一次改變時間(指屬性) */ 
}; 
stat用來判斷沒有打開的文件,而fstat用來判斷打開的文件.咱們使用最多的屬性是st_ 
mode.經過着屬性咱們能夠判斷給定的文件是一個普通文件仍是一個目錄,鏈接等等.能夠 
使用下面幾個宏來判斷. 
S_ISLNK(st_mode):是不是一個鏈接.S_ISREG是不是一個常規文件.S_ISDIR是不是一個目 
錄S_ISCHR是不是一個字符設備.S_ISBLK是不是一個塊設備S_ISFIFO是否 是一個FIFO文 
件.S_ISSOCK是不是一個SOCKET文件. 咱們會在下面說明如何使用這幾個宏的. 
3。目錄文件的操做 
在咱們編寫程序的時候,有時候會要獲得咱們當前的工做路徑。C庫函數提供了get 
cwd來解決這個問題。 
#include <unistd.h> 

char *getcwd(char *buffer,size_t size); 
咱們提供一個size大小的buffer,getcwd會把咱們當前的路徑考到buffer中.若是buffer 
過小,函數會返回-1和一個錯誤號. 
Linux提供了大量的目錄操做函數,咱們學習幾個比較簡單和經常使用的函數. 
#include <dirent.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
int mkdir(const char *path,mode_t mode); 
DIR *opendir(const char *path); 
struct dirent *readdir(DIR *dir); 
void rewinddir(DIR *dir); 
off_t telldir(DIR *dir); 
void seekdir(DIR *dir,off_t off); 
int closedir(DIR *dir); 
struct dirent { 
long d_ino; 
off_t d_off; 
unsigned short d_reclen; 
char d_name[NAME_MAX+1]; /* 文件名稱 */ 
mkdir很容易就是咱們建立一個目錄,opendir打開一個目錄爲之後讀作準備.readdir讀一 
個打開的目錄.rewinddir是用來重讀目錄的和咱們學的rewind函數同樣.closedir是關閉 
一個目錄.telldir和seekdir相似與ftee和fseek函數. 
下面咱們開發一個小程序,這個程序有一個參數.若是這個參數是一個文件名,咱們輸出這 
個文件的大小和最後修改的時間,若是是一個目錄咱們輸出這個目錄下全部文件的大小和 
修改時間. 
#include <unistd.h> 
#include <stdio.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <dirent.h> 
#include <time.h> 
static int get_file_size_time(const char *filename) 

struct stat statbuf; 
if(stat(filename,&statbuf)==-1) 

printf("Get stat on %s Error:%s/n", 
filename,strerror(errno)); 
return(-1); 

if(S_ISDIR(statbuf.st_mode))return(1); 
if(S_ISREG(statbuf.st_mode)) 
printf("%s size:%ld bytes/tmodified at %s", 
filename,statbuf.st_size,ctime(&statbuf.st_mtime)); 

return(0); 

int main(int argc,char **argv) 

DIR *dirp; 
struct dirent *direntp; 
int stats; 
if(argc!=2) 

printf("Usage:%s filename/n/a",argv[0]); 
exit(1); 

if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1); 
if((dirp=opendir(argv[1]))==NULL) 

printf("Open Directory %s Error:%s/n", 
argv[1],strerror(errno)); 
exit(1); 

while((direntp=readdir(dirp))!=NULL) 
if(get_file_size_time(direntp-<d_name)==-1)break; 
closedir(dirp); 
exit(1); 

4。管道文件 
Linux提供了許多的過濾和重定向程序,好比more cat 
等等.還提供了< > | <<等等重定向操做符.在這些過濾和重 定向程序當中,都用到了管 
道這種特殊的文件.系統調用pipe能夠建立一個管道. 
#include<unistd.h> 

int pipe(int fildes[2]); 
pipe調用能夠建立一個管道(通訊緩衝區).當調用成功時,咱們能夠訪問文件描述符fild 
es[0],fildes[1].其中fildes[0]是用來讀的文件描述符,而fildes[1]是用來寫的文件描 
述符. 
在實際使用中咱們是經過建立一個子進程,而後一個進程寫,一個進程讀來使用的. 
關於進程通訊的詳細狀況請查看進程通訊 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#define BUFFER 255 
int main(int argc,char **argv) 

char buffer[BUFFER+1]; 
int fd[2]; 
if(argc!=2) 

fprintf(stderr,"Usage:%s string/n/a",argv[0]); 
exit(1); 

if(pipe(fd)!=0) 

fprintf(stderr,"Pipe Error:%s/n/a",strerror(errno)); 
exit(1); 

if(fork()==0) 

close(fd[0]); 
printf("Child[%d] Write to pipe/n/a",getpid()); 
snprintf(buffer,BUFFER,"%s",argv[1]); 
write(fd[1],buffer,strlen(buffer)); 
printf("Child[%d] Quit/n/a",getpid()); 
exit(0); 

else 

close(fd[1]); 
printf("Parent[%d] Read from pipe/n/a",getpid()); 
memset(buffer,'',BUFFER+1); 
read(fd[0],buffer,BUFFER); 
printf("Parent[%d] Read:%s/n",getpid(),buffer); 
exit(1); 


爲了實現重定向操做,咱們須要調用另一個函數dup2. 
#include <unistd.h> 

int dup2(int oldfd,int newfd); 
dup2將用oldfd文件描述符來代替newfd文件描述符,同時關閉newfd文件描述符.也就是說 

全部向newfd操做都轉到oldfd上面.下面咱們學習一個例子,這個例子將標準輸出重定向 
到一個文件. 
#include <unistd.h> 
#include <stdio.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#define BUFFER_SIZE 1024 
int main(int argc,char **argv) 

int fd; 
char buffer[BUFFER_SIZE]; 
if(argc!=2) 

fprintf(stderr,"Usage:%s outfilename/n/a",argv[0]); 
exit(1); 

if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1) 

fprintf(stderr,"Open %s Error:%s/n/a",argv[1],strerror(errno)); 
exit(1); 

if(dup2(fd,STDOUT_FILENO)==-1) 

fprintf(stderr,"Redirect Standard Out Error:%s/n/a",strerror(errno)); 
exit(1); 

fprintf(stderr,"Now,please input string"); 
fprintf(stderr,"(To quit use CTRL+D)/n"); 
while(1) 

fgets(buffer,BUFFER_SIZE,stdin); 
if(feof(stdin))break; 
write(STDOUT_FILENO,buffer,strlen(buffer)); 

exit(0); 

好了,文件一章咱們就暫時先討論到這裏,學習好了文件的操做咱們其實已經能夠寫出一 
些比較有用的程序了.咱們能夠編寫一個實現例如dir,mkdir,cp,mv等等經常使用的文件操做 
命令了. 
想不想本身寫幾個試一試呢? 

4)程序設計入門--時間概念 
前言:Linux下的時間概念 
這一章咱們學習Linux的時間表示和計算函數 
時間的表示 
時間的測量 
計時器的使用 
1。時間表示 在程序當中,咱們常常要輸出系統當前的時間,好比咱們使用date命令 
的輸出結果.這個時候咱們能夠使用下面兩個函數 
#include <time.h> 

time_t time(time_t *tloc); 
char *ctime(const time_t *clock); 
time函數返回從1970年1月1日0點以來的秒數.存儲在time_t結構之中.不過這個函數的返 
回值對於咱們來講沒有什麼實際意義.這個時候咱們使用第二個函數將秒數轉化爲字符串 
.. 這個函數的返回類型是固定的:一個可能值爲. Thu Dec 7 14:58:59 2000 這個字符串 
的長度是固定的爲26 
2。時間的測量 有時候咱們要計算程序執行的時間.好比咱們要對算法進行時間分析 
..這個時候能夠使用下面這個函數. 
#include <sys/time.h> 

int gettimeofday(struct timeval *tv,struct timezone *tz); 
strut timeval { 
long tv_sec; /* 秒數 */ 
long tv_usec; /* 微秒數 */ 
}; 
gettimeofday將時間保存在結構tv之中.tz通常咱們使用NULL來代替. 
#include <sys/time.h< 
#include <stdio.h< 
#include <math.h< 
void function() 

unsigned int i,j; 
double y; 
for(i=0;i<1000;i++) 
for(j=0;j<1000;j++) 
y=sin((double)i); 

main() 

struct timeval tpstart,tpend; 
float timeuse; 
gettimeofday(&tpstart,NULL); 
function(); 
gettimeofday(&tpend,NULL); 
timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ 
tpend.tv_usec-tpstart.tv_usec; 
timeuse/=1000000; 
printf("Used Time:%f/n",timeuse); 
exit(0); 

這個程序輸出函數的執行時間,咱們能夠使用這個來進行系統性能的測試,或者是函數算 
法的效率分析.在我機器上的一個輸出結果是: Used Time:0.556070 
3。計時器的使用 Linux操做系統爲每個進程提供了3個內部間隔計時器. 
ITIMER_REAL:減小實際時間.到時的時候發出SIGALRM信號. 
ITIMER_VIRTUAL:減小有效時間(進程執行的時間).產生SIGVTALRM信號. 
ITIMER_PROF:減小進程的有效時間和系統時間(爲進程調度用的時間).這個常常和上面一 
個使用用來計算系統內核時間和用戶時間.產生SIGPROF信號. 
具體的操做函數是: 
#include <sys/time.h> 
int getitimer(int which,struct itimerval *value); 
int setitimer(int which,struct itimerval *newval, 
struct itimerval *oldval); 
struct itimerval { 
struct timeval it_interval; 
struct timeval it_value; 

getitimer函數獲得間隔計時器的時間值.保存在value中 setitimer函數設置間隔計時器 
的時間值爲newval.並將舊值保存在oldval中. which表示使用三個計時器中的哪個. 
itimerval結構中的it_value是減小的時間,當這個值爲0的時候就發出相應的信號了. 然 
後設置爲it_interval值. 
#include <sys/time.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <signal.h> 
#include <string.h> 
#define PROMPT "時間已通過去了兩秒鐘/n/a" 
char *prompt=PROMPT; 
unsigned int len; 
void prompt_info(int signo) 

write(STDERR_FILENO,prompt,len); 

void init_sigaction(void) 

struct sigaction act; 
act.sa_handler=prompt_info; 
act.sa_flags=0; 
sigemptyset(&act.sa_mask); 
sigaction(SIGPROF,&act,NULL); 

void init_time() 

struct itimerval value; 
value.it_value.tv_sec=2; 
value.it_value.tv_usec=0; 
value.it_interval=value.it_value; 
setitimer(ITIMER_PROF,&value,NULL); 

int main() 

len=strlen(prompt); 
init_sigaction(); 
init_time(); 
while(1); 
exit(0); 

這個程序每執行兩秒中以後會輸出一個提示. 

5)Linux程序設計入門--信號處理 
Linux下的信號事件 
前言:這一章咱們討論一下Linux下的信號處理函數. 
Linux下的信號處理函數: 
信號的產生 
信號的處理 
其它信號函數 
一個實例 
1。信號的產生 
Linux下的信號能夠類比於DOS下的INT或者是Windows下的事件.在有一個信號發生時 
候相信的信號就會發送給相應的進程.在Linux下的信號有如下幾個. 咱們使用 kill -l 
命令能夠獲得如下的輸出結果: 
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 
30) SIGPWR 
關於這些信號的詳細解釋請查看man 7 signal的輸出結果. 信號事件的發生有兩個來源 
:一個是硬件的緣由(好比咱們按下了鍵盤),一個是軟件的緣由(好比咱們使用系統函數或 
者是命令發出信號). 最經常使用的四個發出信號的系統函數是kill, raise, alarm和setit 
imer函數. setitimer函數咱們在計時器的使用 那一章再學習. 
#include <sys/types.h> 
#include <signal.h> 
#include <unistd.h> 
int kill(pid_t pid,int sig); 
int raise(int sig); 
unisigned int alarm(unsigned int seconds); 
kill系統調用負責向進程發送信號sig. 
若是pid是正數,那麼向信號sig被髮送到進程pid. 
若是pid等於0,那麼信號sig被髮送到因此和pid進程在同一個進程組的進程 
若是pid等於-1,那麼信號發給全部的進程表中的進程,除了最大的哪一個進程號. 
若是pid因爲-1,和0同樣,只是發送進程組是-pid. 
咱們用最多的是第一個狀況.還記得咱們在守護進程那一節的例子嗎?咱們那個時候用這 
個函數殺死了父進程守護進程的建立 
raise系統調用向本身發送一個sig信號.咱們能夠用上面那個函數來實現這個功能的. 
alarm函數和時間有點關係了,這個函數能夠在seconds秒後向本身發送一個SIGALRM信號 
.. 下面這個函數會有什麼結果呢? 
#include <unistd.h> 
main() 

unsigned int i; 
alarm(1); 
for(i=0;1;i++) 
printf("I=%d",i); 

SIGALRM的缺省操做是結束進程,因此程序在1秒以後結束,你能夠看看你的最後I值爲多少 
,來比較一下你們的系統性能差別(個人是2232). 
2。信號操做 有時候咱們但願進程正確的執行,而不想進程受到信號的影響,好比我 
們但願上面那個程序在1秒鐘以後不結束.這個時候咱們就要進行信號的操做了. 
信號操做最經常使用的方法是信號屏蔽.信號屏蔽要用到下面的幾個函數. 
#include <signal.h> 
int sigemptyset(sigset_t *set); 
int sigfillset(sigset_t *set); 
int sigaddset(sigset_t *set,int signo); 
int sigdelset(sigset_t *set,int signo); 
int sigismember(sigset_t *set,int signo); 
int sigprocmask(int how,const sigset_t *set,sigset_t *oset); 
sigemptyset函數初始化信號集合set,將set設置爲空.sigfillset也初始化信號集合,只 
是將信號集合設置爲全部信號的集合.sigaddset將信號signo加入到信號集合之中,sigd 
elset將信號從信號集合中刪除.sigismember查詢信號是否在信號集合之中. 
sigprocmask是最爲關鍵的一個函數.在使用以前要先設置好信號集合set.這個函數的做 
用是將指定的信號集合set加入到進程的信號阻塞集合之中去,若是提供了oset那麼當前 
的進程信號阻塞集合將會保存在oset裏面.參數how決定函數的操做方式. 
SIG_BLOCK:增長一個信號集合到當前進程的阻塞集合之中. 
SIG_UNBLOCK:從當前的阻塞集合之中刪除一個信號集合. 
SIG_SETMASK:將當前的信號集合設置爲信號阻塞集合. 
以一個實例來解釋使用這幾個函數. 
#include <signal.h> 
#include <stdio.h> 
#include <math.h> 
#include <stdlib.h> 
int main(int argc,char **argv) 

double y; 
sigset_t intmask; 
int i,repeat_factor; 
if(argc!=2) 

fprintf(stderr,"Usage:%s repeat_factor/n/a",argv[0]); 
exit(1); 

if((repeat_factor=atoi(argv[1]))<1)repeat_factor=10; 
sigemptyset(&intmask); /* 將信號集合設置爲空 */ 
sigaddset(&intmask,SIGINT); /* 加入中斷 Ctrl+C 信號*/ 
while(1) 

/*阻塞信號,咱們不但願保存原來的集合因此參數爲NULL*/ 
sigprocmask(SIG_BLOCK,&intmask,NULL); 
fprintf(stderr,"SIGINT signal blocked/n"); 
for(i=0;i<repeat_factor;i++)y=sin((double)i); 
fprintf(stderr,"Blocked calculation is finished/n"); 
/* 取消阻塞 */ 
sigprocmask(SIG_UNBLOCK,&intmask,NULL); 
fprintf(stderr,"SIGINT signal unblocked/n"); 
for(i=0;i<repeat_factor;i++)y=sin((double)i); 
fprintf(stderr,"Unblocked calculation is finished/n"); 

exit(0); 

程序在運行的時候咱們要使用Ctrl+C來結束.若是咱們在第一計算的時候發出SIGINT信號 
,因爲信號已經屏蔽了,因此程序沒有反映.只有到信號被取消阻塞的時候程序纔會結束. 
注意咱們只要發出一次SIGINT信號就能夠了,由於信號屏蔽只是將信號加入到信號阻塞 
集合之中,並無丟棄這個信號.一旦信號屏蔽取消了,這個信號就會發生做用. 
有時候咱們但願對信號做出及時的反映的,好比當擁護按下Ctrl+C時,咱們不想什麼事情 
也不作,咱們想告訴用戶你的這個操做很差,請不要重試,而不是什麼反映也沒有的. 這個 
時候咱們要用到sigaction函數. 
#include <signal.h> 

int sigaction(int signo,const struct sigaction *act, 
struct sigaction *oact); 
struct sigaction { 
void (*sa_handler)(int signo); 
void (*sa_sigaction)(int siginfo_t *info,void *act); 
sigset_t sa_mask; 
int sa_flags; 
void (*sa_restore)(void); 

這個函數和結構看起來是否是有點恐怖呢.不要被這個嚇着了,其實這個函數的使用至關 
簡單的.咱們先解釋一下各個參數的含義. signo很簡單就是咱們要處理的信號了,能夠是 
任何的合法的信號.有兩個信號不可以使用(SIGKILL和SIGSTOP). act包含咱們要對這個 
信號進行如何處理的信息.oact更簡單了就是之前對這個函數的處理信息了,主要用來保 
存信息的,通常用NULL就OK了. 
信號結構有點複雜.沒關係咱們慢慢的學習. 
sa_handler是一個函數型指針,這個指針指向一個函數,這個函數有一個參數.這個函數就 
是咱們要進行的信號操做的函數. sa_sigaction,sa_restore和sa_handler差很少的,只 
是參數不一樣罷了.這兩個元素咱們不多使用,就無論了. 
sa_flags用來設置信號操做的各個狀況.通常設置爲0好了.sa_mask咱們已經學習過了 
在使用的時候咱們用sa_handler指向咱們的一個信號操做函數,就能夠了.sa_handler有 
兩個特殊的值:SIG_DEL和SIG_IGN.SIG_DEL是使用缺省的信號操做函數,而SIG_IGN是使用 
忽略該信號的操做函數. 
這個函數複雜,咱們使用一個實例來講明.下面這個函數能夠捕捉用戶的CTRL+C信號.並輸 
出一個提示語句. 
#include <signal.h> 
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 
#include <unistd.h> 
#define PROMPT "你想終止程序嗎?" 
char *prompt=PROMPT; 
void ctrl_c_op(int signo) 

write(STDERR_FILENO,prompt,strlen(prompt)); 

int main() 

struct sigaction act; 
act.sa_handler=ctrl_c_op; 
sigemptyset(&act.sa_mask); 
act.sa_flags=0; 
if(sigaction(SIGINT,&act,NULL)<0) 

fprintf(stderr,"Install Signal Action Error:%s/n/a",strerror(errno)); 
exit(1); 

while(1); 

在上面程序的信號操做函數之中,咱們使用了write函數而沒有使用fprintf函數.是由於 
咱們要考慮到下面這種狀況.若是咱們在信號操做的時候又有一個信號發生,那麼程序該 
如何運行呢? 爲了處理在信號處理函數運行的時候信號的發生,咱們須要設置sa_mask成 
員. 咱們將咱們要屏蔽的信號添加到sa_mask結構當中去,這樣這些函數在信號處理的時 
候就會被屏蔽掉的. 
3。其它信號函數 因爲信號的操做和處理比較複雜,咱們再介紹幾個信號操做函數. 

#include <unistd.h> 
#include <signal.h> 
int pause(void); 
int sigsuspend(const sigset_t *sigmask); 
pause函數很簡單,就是掛起進程直到一個信號發生了.而sigsuspend也是掛起進程只是在 
調用的時候用sigmask取代當前的信號阻塞集合. 
#include <sigsetjmp> 
int sigsetjmp(sigjmp_buf env,int val); 
void siglongjmp(sigjmp_buf env,int val); 
還記得goto函數或者是setjmp和longjmp函數嗎.這兩個信號跳轉函數也能夠實現程序的 
跳轉讓咱們能夠從函數之中跳轉到咱們須要的地方. 
因爲上面幾個函數,咱們不多遇到,因此只是說明了一下,詳細狀況請查看聯機幫助. 
4。一個實例 還記得咱們在守護進程建立的哪一個程序嗎?守護進程在這裏咱們把那個 
程序增強一下. 下面這個程序會在也能夠檢查用戶的郵件.不過提供了一個開關,若是用 
戶不想程序提示有新的郵件到來,能夠向程序發送SIGUSR2信號,若是想程序提供提示能夠 
發送SIGUSR1信號. 
#include <unistd.h> 
#include <stdio.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <signal.h> 
#include <string.h> 
#include <pwd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
/* Linux 的默任我的的郵箱地址是 /var/spool/mail/ */ 
#define MAIL_DIR "/var/spool/mail/" 
/* 睡眠10秒鐘 */ 
#define SLEEP_TIME 10 
#define MAX_FILENAME 255 
unsigned char notifyflag=1; 
long get_file_size(const char *filename) 

struct stat buf; 
if(stat(filename,&;buf)==-1) 

if(errno==ENOENT)return 0; 
else return -1; 

return (long)buf.st_size; 

void send_mail_notify(void) 

fprintf(stderr,"New mail has arrived7/n"); 

void turn_on_notify(int signo) 

notifyflag=1; 

void turn_off_notify(int signo) 

notifyflag=0; 

int check_mail(const char *filename) 

long old_mail_size,new_mail_size; 
sigset_t blockset,emptyset; 
sigemptyset(&;blockset); 
sigemptyset(&;emptyset); 
sigaddset(&;blockset,SIGUSR1); 
sigaddset(&;blockset,SIGUSR2); 
old_mail_size=get_file_size(filename); 
if(old_mail_size<0)return 1; 
if(old_mail_size>0) send_mail_notify(); 
sleep(SLEEP_TIME); 
while(1) 

if(sigprocmask(SIG_BLOCK,&;blockset,NULL)<0) return 1; 
while(notifyflag==0)sigsuspend(&;emptyset); 
if(sigprocmask(SIG_SETMASK,&;emptyset,NULL)<0) return 1; 
new_mail_size=get_file_size(filename); 
if(new_mail_size>old_mail_size)send_mail_notify; 
old_mail_size=new_mail_size; 
sleep(SLEEP_TIME); 


int main(void) 

char mailfile[MAX_FILENAME]; 
struct sigaction newact; 
struct passwd *pw; 
if((pw=getpwuid(getuid()))==NULL) 

fprintf(stderr,"Get Login Name Error:%s/n/a",strerror(errno)); 
exit(1); 

strcpy(mailfile,MAIL_DIR); 
strcat(mailfile,pw->pw_name); 
newact.sa_handler=turn_on_notify; 
newact.sa_flags=0; 
sigemptyset(&;newact.sa_mask); 
sigaddset(&;newact.sa_mask,SIGUSR1); 
sigaddset(&;newact.sa_mask,SIGUSR2); 
if(sigaction(SIGUSR1,&;newact,NULL)<0) 
fprintf(stderr,"Turn On Error:%s/n/a",strerror(errno)); 
newact.sa_handler=turn_off_notify; 
if(sigaction(SIGUSR1,&;newact,NULL)<0) 
fprintf(stderr,"Turn Off Error:%s/n/a",strerror(errno)); 
check_mail(mailfile); 
exit(0); 

信號操做是一件很是複雜的事情,比咱們想象之中的複雜程度還要複雜,若是你想完全的 
弄清楚信號操做的各個問題,那麼除了大量的練習之外還要多看聯機手冊.不過若是咱們 
只是通常的使用的話,有了上面的幾個函數也就差很少了. 咱們就介紹到這裏了. 

6)Linux程序設計入門--消息管理 
前言:Linux下的進程通訊(IPC) 
Linux下的進程通訊(IPC) 
POSIX無名信號量 
System V信號量 
System V消息隊列 
System V共享內存 
1。POSIX無名信號量 若是你學習過操做系統,那麼確定熟悉PV操做了.PV操做是原子 
操做.也就是操做是不能夠中斷的,在必定的時間內,只可以有一個進程的代碼在CPU上面 
執行.在系統當中,有時候爲了順利的使用和保護共享資源,你們提出了信號的概念. 假設 
咱們要使用一臺打印機,若是在同一時刻有兩個進程在向打印機輸出,那麼最終的結果會 
是什麼呢.爲了處理這種狀況,POSIX標準提出了有名信號量和無名信號量的概念,因爲Li 
nux只實現了無名信號量,咱們在這裏就只是介紹無名信號量了. 信號量的使用主要是用 
來保護共享資源,使的資源在一個時刻只有一個進程所擁有.爲此咱們能夠使用一個信號 
燈.當信號燈的值爲某個值的時候,就代表此時資源不能夠使用.不然就表>示能夠使用. 
爲了提供效率,系統提供了下面幾個函數 
POSIX的無名信號量的函數有如下幾個: 
#include <semaphore.h> 
int sem_init(sem_t *sem,int pshared,unsigned int value); 
int sem_destroy(sem_t *sem); 
int sem_wait(sem_t *sem); 
int sem_trywait(sem_t *sem); 
int sem_post(sem_t *sem); 
int sem_getvalue(sem_t *sem); 
sem_init建立一個信號燈,並初始化其值爲value.pshared決定了信號量可否在幾個進程 
間共享.因爲目前Linux尚未實現進程間共享信號燈,因此這個值只可以取0. sem_dest 
roy是用來刪除信號燈的.sem_wait調用將阻塞進程,直到信號燈的值大於0.這個函數返回 
的時候自動的將信號燈的值的件一.sem_post和sem_wait相反,是將信號燈的內容加一同 
時發出信號喚醒等待的進程..sem_trywait和sem_wait相同,不過不阻塞的,當信號燈的值 
爲0的時候返回EAGAIN,表示之後重試.sem_getvalue獲得信號燈的值. 
因爲Linux不支持,咱們沒有辦法用源程序解釋了. 
這幾個函數的使用至關簡單的.好比咱們有一個程序要向一個系統打印機打印兩頁.咱們 
首先建立一個信號燈,並使其初始值爲1,表示咱們有一個資源可用.而後一個進程調用se 
m_wait因爲這個時候信號燈的值爲1,因此這個函數返回,打印機開始打印了,同時信號燈 
的值爲0 了. 若是第二個進程要打印,調用sem_wait時候,因爲信號燈的值爲0,資源不可 
用,因而被阻塞了.當第一個進程打印完成之後,調用sem_post信號燈的值爲1了,這個時候 
系統通知第二個進程,因而第二個進程的sem_wait返回.第二個進程開始打印了. 
不過咱們能夠使用線程來解決這個問題的.咱們會在後面解釋什麼是線程的.編譯包含上 
面這幾個函數的程序要加上 -lrt選賢,以鏈接librt.so庫 
2。System V信號量 爲了解決上面哪一個問題,咱們也能夠使用System V信號量.很幸運的 
是Linux實現了System V信號量.這樣咱們就能夠用實例來解釋了. System V信號量的函 
數主要有下面幾個. 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 
key_t ftok(char *pathname,char proj); 
int semget(key_t key,int nsems,int semflg); 
int semctl(int semid,int semnum,int cmd,union semun arg); 
int semop(int semid,struct sembuf *spos,int nspos); 
struct sembuf { 
short sem_num; /* 使用那一個信號 */ 
short sem_op; /* 進行什麼操做 */ 
short sem_flg; /* 操做的標誌 */ 
}; 
ftok函數是根據pathname和proj來建立一個關鍵字.semget建立一個信號量.成功時返回 
信號的ID,key是一個關鍵字,能夠是用ftok建立的也能夠是IPC_PRIVATE代表由系統選用 
一個關鍵字. nsems代表咱們建立的信號個數.semflg是建立的權限標誌,和咱們建立一個 
文件的標誌相同. 
semctl對信號量進行一系列的控制.semid是要操做的信號標誌,semnum是信號的個數,cm 
d是操做的命令.常常用的兩個值是:SETVAL(設置信號量的值)和IPC_RMID(刪除信號燈). 
arg是一個給cmd的參數. 
semop是對信號進行操做的函數.semid是信號標誌,spos是一個操做數組代表要進行什麼 
操做,nspos代表數組的個數. 若是sem_op大於0,那麼操做將sem_op加入到信號量的值中 
,並喚醒等待信號增長的進程. 若是爲0,當信號量的值是0的時候,函數返回,不然阻塞直 
到信號量的值爲0. 若是小於0,函數判斷信號量的值加上這個負值.若是結果爲0喚醒等待 
信號量爲0的進程,若是小與0函數阻塞.若是大於0,那麼從信號量裏面減去這個值並返回 
.. 
下面咱們一以一個實例來講明這幾個函數的使用方法.這個程序用標準錯誤輸出來代替我 
們用的打印機. 
#include <stdio.h> 
#include <unistd.h> 
#include <limits.h> 
#include <errno.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sys/stat.h> 
#include <sys/wait.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 
#define PERMS S_IRUSR|S_IWUSR 
void init_semaphore_struct(struct sembuf *sem,int semnum, 
int semop,int semflg) 

/* 初始話信號燈結構 */ 
sem->sem_num=semnum; 
sem->sem_op=semop; 
sem->sem_flg=semflg; 

int del_semaphore(int semid) 

/* 信號燈並不隨程序的結束而被刪除,若是咱們沒刪除的話(將1改成0) 
能夠用ipcs命令查看到信號燈,用ipcrm能夠刪除信號燈的 
*/ 
#if 1 
return semctl(semid,0,IPC_RMID); 
#endif 

int main(int argc,char **argv) 

char buffer[MAX_CANON],*c; 
int i,n; 
int semid,semop_ret,status; 
pid_t childpid; 
struct sembuf semwait,semsignal; 
if((argc!=2)||((n=atoi(argv[1]))<1)) 

fprintf(stderr,"Usage:%s number/n/a",argv[0]); 
exit(1); 

/* 使用IPC_PRIVATE 表示由系統選擇一個關鍵字來建立 */ 
/* 建立之後信號燈的初始值爲0 */ 
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1) 

fprintf(stderr,"[%d]:Acess Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 
exit(1); 

/* semwait是要求資源的操做(-1) */ 
init_semaphore_struct(&semwait,0,-1,0); 
/* semsignal是釋放資源的操做(+1) */ 
init_semaphore_struct(&semsignal,0,1,0); 
/* 開始的時候有一個系統資源(一個標準錯誤輸出) */ 
if(semop(semid,&semsignal,1)==-1) 

fprintf(stderr,"[%d]:Increment Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 
if(del_semaphore(semid)==-1) 
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 
exit(1); 

/* 建立一個進程鏈 */ 
for(i=0;i<n;i++) 
if(childpid=fork()) break; 
sprintf(buffer,"[i=%d]-->[Process=%d]-->[Parent=%d]-->[Child=%d]/n", 
i,getpid(),getppid(),childpid); 
c=buffer; 
/* 這裏要求資源,進入原子操做 */ 
while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR)); 
if(semop_ret==-1) 

fprintf(stderr,"[%d]:Decrement Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 

else 

while(*c!='')fputc(*c++,stderr); 
/* 原子操做完成,趕快釋放資源 */ 
while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR)); 
if(semop_ret==-1) 
fprintf(stderr,"[%d]:Increment Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 

/* 不可以在其餘進程反問信號燈的時候,咱們刪除了信號燈 */ 
while((wait(&status)==-1)&&(errno==EINTR)); 
/* 信號燈只可以被刪除一次的 */ 
if(i==1) 
if(del_semaphore(semid)==-1) 
fprintf(stderr,"[%d]:Destroy Semaphore Error:%s/n/a", 
getpid(),strerror(errno)); 
exit(0); 

信號燈的主要用途是保護臨界資源(在一個時刻只被一個進程所擁有). 
3。SystemV消息隊列 爲了便於進程之間通訊,咱們能夠使用管道通訊 SystemV也提供了 
一些函數來實現進程的通訊.這就是消息隊列. 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
int msgget(key_t key,int msgflg); 
int msgsnd(int msgid,struct msgbuf *msgp,int msgsz,int msgflg); 
int msgrcv(int msgid,struct msgbuf *msgp,int msgsz, 
long msgtype,int msgflg); 
int msgctl(Int msgid,int cmd,struct msqid_ds *buf); 

struct msgbuf { 
long msgtype; /* 消息類型 */ 
....... /* 其餘數據類型 */ 

msgget函數和semget同樣,返回一個消息隊列的標誌.msgctl和semctl是對消息進行控制 
.. msgsnd和msgrcv函數是用來進行消息通信的.msgid是接受或者發送的消息隊列標誌. 
msgp是接受或者發送的內容.msgsz是消息的大小. 結構msgbuf包含的內容是至少有一個 
爲msgtype.其餘的成分是用戶定義的.對於發送函數msgflg指出緩衝區用完時候的操做. 
接受函數指出無消息時候的處理.通常爲0. 接收函數msgtype指出接收消息時候的操做. 

若是msgtype=0,接收消息隊列的第一個消息.大於0接收隊列中消息類型等於這個值的第 
一個消息.小於0接收消息隊列中小於或者等於msgtype絕對值的全部消息中的最小一個消 
息. 咱們以一個實例來解釋進程通訊.下面這個程序有server和client組成.先運行服務 
端後運行客戶端. 
服務端 server.c 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/stat.h> 
#include <sys/msg.h> 
#define MSG_FILE "server.c" 
#define BUFFER 255 
#define PERM S_IRUSR|S_IWUSR 
struct msgtype { 
long mtype; 
char buffer[BUFFER+1]; 
}; 
int main() 

struct msgtype msg; 
key_t key; 
int msgid; 
if((key=ftok(MSG_FILE,'a'))==-1) 

fprintf(stderr,"Creat Key Error:%s/a/n",strerror(errno)); 
exit(1); 

if((msgid=msgget(key,PERM|IPC_CREAT|IPC_EXCL))==-1) 

fprintf(stderr,"Creat Message Error:%s/a/n",strerror(errno)); 
exit(1); 

while(1) 

msgrcv(msgid,&msg,sizeof(struct msgtype),1,0); 
fprintf(stderr,"Server Receive:%s/n",msg.buffer); 
msg.mtype=2; 
msgsnd(msgid,&msg,sizeof(struct msgtype),0); 

exit(0); 

---------------------------------------------------------------------------- 
---- 
客戶端(client.c) 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
#include <sys/stat.h> 
#define MSG_FILE "server.c" 
#define BUFFER 255 
#define PERM S_IRUSR|S_IWUSR 
struct msgtype { 
long mtype; 
char buffer[BUFFER+1]; 
}; 
int main(int argc,char **argv) 

struct msgtype msg; 
key_t key; 
int msgid; 
if(argc!=2) 

fprintf(stderr,"Usage:%s string/n/a",argv[0]); 
exit(1); 

if((key=ftok(MSG_FILE,'a'))==-1) 

fprintf(stderr,"Creat Key Error:%s/a/n",strerror(errno)); 
exit(1); 

if((msgid=msgget(key,PERM))==-1) 

fprintf(stderr,"Creat Message Error:%s/a/n",strerror(errno)); 
exit(1); 

msg.mtype=1; 
strncpy(msg.buffer,argv[1],BUFFER); 
msgsnd(msgid,&msg,sizeof(struct msgtype),0); 
memset(&msg,'',sizeof(struct msgtype)); 
msgrcv(msgid,&msg,sizeof(struct msgtype),2,0); 
fprintf(stderr,"Client receive:%s/n",msg.buffer); 
exit(0); 

注意服務端建立的消息隊列最後沒有刪除,咱們要使用ipcrm命令來刪除的. 
4。SystemV共享內存 還有一個進程通訊的方法是使用共享內存.SystemV提供瞭如下幾個 
函數以實現共享內存. 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
int shmget(key_t key,int size,int shmflg); 
void *shmat(int shmid,const void *shmaddr,int shmflg); 
int shmdt(const void *shmaddr); 
int shmctl(int shmid,int cmd,struct shmid_ds *buf); 
shmget和shmctl沒有什麼好解釋的.size是共享內存的大小. shmat是用來鏈接共享內存 
的.shmdt是用來斷開共享內存的.不要被共享內存詞語嚇倒,共享內存其實很容易實現和 
使用的.shmaddr,shmflg咱們只要用0代替就能夠了.在使用一個共享內存以前咱們調用s 
hmat獲得共享內存的開始地址,使用結束之後咱們使用shmdt斷開這個內存. 
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 
#include <unistd.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#define PERM S_IRUSR|S_IWUSR 
int main(int argc,char **argv) 

int shmid; 
char *p_addr,*c_addr; 
if(argc!=2) 

fprintf(stderr,"Usage:%s/n/a",argv[0]); 
exit(1); 

if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1) 

fprintf(stderr,"Create Share Memory Error:%s/n/a",strerror(errno)); 
exit(1); 

if(fork()) 

p_addr=shmat(shmid,0,0); 
memset(p_addr,'',1024); 
strncpy(p_addr,argv[1],1024); 
exit(0); 

else 

c_addr=shmat(shmid,0,0); 
printf("Client get %s",c_addr); 
exit(0); 


這個程序是父進程將參數寫入到共享內存,而後子進程把內容讀出來.最後咱們要使用ip 
crm釋放資源的.先用ipcs找出ID而後用ipcrm shm ID刪除. 
後記: 
進程通訊(IPC)是網絡程序的基礎,在不少的網絡程序當中會大量的使用進程通訊的概念 
和知識.其實進程通訊是一件很是複雜的事情,我在這裏只是簡單的介紹了一下.若是你想 
學習進程通訊的詳細知識,最好的辦法是本身不斷的寫程序和看聯機手冊.如今網絡上有 
了不少的知識能夠去參考.惋惜我看到的不少都是英文編寫的.若是你找到了有中文的版 
本請儘快告訴我.謝謝! 

7)Linux程序設計入門--線程操做 
前言:Linux下線程的建立 
介紹在Linux下線程的建立和基本的使用. Linux下的線程是一個很是複雜的問題,由 
於我對線程的學習不時很好,我在這裏只是簡單的介紹線程的建立和基本的使用,關於線 
程的高級使用(如線程的屬性,線程的互斥,線程的同步等等問題)能夠參考我後面給出的 
資料. 如今關於線程的資料在網絡上能夠找到許多英文資料,後面我羅列了許多連接,對 
線程的高級屬性感興趣的話能夠參考一下. 等到我對線程的瞭解比較深入的時候,我回來 
完成這篇文章.若是您對線程瞭解的詳盡我也很是高興可以由您來完善. 
先介紹什麼是線程.咱們編寫的程序大多數能夠當作是單線程的.就是程序是按照必定的 
順序來執行.若是咱們使用線程的話,程序就會在咱們建立線成的地方分叉,變成兩個"程 
序"在執行.粗略的看來好象和子進程差很少的,其實否則.子進程是經過拷貝父進程的地 
址空間來執行的.而線程是經過共享程序代碼來執行的,講的通俗一點就是線程的相同的 
代碼會被執行幾回.使用線程的好處是能夠節省資源,因爲線程是經過共享代碼的,因此沒 
有進程調度那麼複雜. 
線程的建立和使用 
線程的建立是用下面的幾個函數來實現的. 
#include <pthread.h> 
int pthread_create(pthread_t *thread,pthread_attr_t *attr, 
void *(*start_routine)(void *),void *arg); 
void pthread_exit(void *retval); 
int pthread_join(pthread *thread,void **thread_return); 
pthread_create建立一個線程,thread是用來代表建立線程的ID,attr指出線程建立時候 
的屬性,咱們用NULL來代表使用缺省屬性.start_routine函數指針是線程建立成功後開始 
執行的函數,arg是這個函數的惟一一個參數.代表傳遞給start_routine的參數. pthrea 
d_exit函數和exit函數相似用來退出線程.這個函數結束線程,釋放函數的資源,並在最後 
阻塞,直到其餘線程使用pthread_join函數等待它.而後將*retval的值傳遞給**thread_ 
return.因爲這個函數釋放因此的函數資源,因此retval不可以指向函數的局部變量. pt 
hread_join和wait調用同樣用來等待指定的線程. 下面咱們使用一個實例來解釋一下使 
用方法.在實踐中,咱們常常要備份一些文件.下面這個程序能夠實現當前目錄下的全部文 
件備份.備份後的後綴名爲bak 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <pthread.h> 
#include <dirent.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/time.h> 
#define BUFFER 512 
struct copy_file { 
int infile; 
int outfile; 
}; 
void *copy(void *arg) 

int infile,outfile; 
int bytes_read,bytes_write,*bytes_copy_p; 
char buffer[BUFFER],*buffer_p; 
struct copy_file *file=(struct copy_file *)arg; 
infile=file->infile; 
outfile=file->outfile; 
/* 由於線程退出時,全部的變量空間都要被釋放,因此咱們只好本身分配內存了 */ 
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL); 
bytes_read=bytes_write=0; 
*bytes_copy_p=0; 
/* 還記得怎麼拷貝文件嗎 */ 
while((bytes_read=read(infile,buffer,BUFFER))!=0) 

if((bytes_read==-1)&&(errno!=EINTR))break; 
else if(bytes_read>0) 

buffer_p=buffer; 
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0) 

if((bytes_write==-1)&&(errno!=EINTR))break; 
else if(bytes_write==bytes_read)break; 
else if(bytes_write>0) 

buffer_p+=bytes_write; 
bytes_read-=bytes_write; 


if(bytes_write==-1)break; 
*bytes_copy_p+=bytes_read; 


close(infile); 
close(outfile); 
pthread_exit(bytes_copy_p); 

int main(int argc,char **argv) 

pthread_t *thread; 
struct copy_file *file; 
int byte_copy,*byte_copy_p,num,i,j; 
char filename[BUFFER]; 
struct dirent **namelist; 
struct stat filestat; 
/* 獲得當前路徑下面全部的文件(包含目錄)的個數 */ 
if((num=scandir(".",&namelist,0,alphasort))<0) 

fprintf(stderr,"Get File Num Error:%s/n/a",strerror(errno)); 
exit(1)
html




 


1。linux編程所用的一些工具
GCC 中文手冊
http://www.nbfan.com/forum/dispbbs....&ID=1433&page=1
GNU make 指南
http://www.linuxsir.org/bbs/showthr...&threadid=40431
autoconf-2.57手冊(英文)
http://www.gnu.org/software/autocon...toconf_toc.html
Autoconf-2.13手冊
http://www.linuxforum.net/books/autoconf.html
使用 automake
http://263.aka.org.cn/Lectures/002/...-2.1.4/230.html
使用CVS進行版本管理
http://www.linuxforum.net/books/cvsintro.html
CVS用後感
http://www.linuxsir.org/bbs/showthread.php?t=170538

linux下經常使用調試工具:
很是好的gdb教程,強烈推薦:
http://www.linuxsir.org/bbs/showthread.php?t=171156
GDB英文文檔
http://oldsite.linuxaid.com.cn/deve...howdev.jsp?i=49
gdb基本用法
http://www.linuxsir.com/bbs/showthr...&threadid=45731
gdb的官方文檔
http://www.gnu.org/software/gdb/documentation/
 
linux編程基礎:
要入門先看這個,Linux下C語言編程基礎知識
http://www.linuxsir.com/bbs/showthr...=&threadid=1005
Linux 下 C 語言編程
http://www.linuxsir.org/bbs/showthr...=&threadid=7192
Linux下的C++編程
http://www.linuxsir.org/bbs/showthr...6723#post276723
linux下的應用程序開發
http://www.lisoleg.net/lisoleg/applications/index.html

參考書籍
Linux程序員指南(The Linux Programmer's Guide)
http://www.linuxhq.com/guides/LPG/lpg.html
UNIX編程環境(The UNIX Programming Environment)
http://www.iu.hio.no/~mark/unix/unix_toc.html
UNIX編程的藝術(The Art of Unix Programming)
http://www.catb.org/~esr/writings/taoup/html/

進程與線程
Linux下的多進程編程初步
http://www.china-pub.com/computers/emook/0439/info.htm
多進程編程
http://www.linuxsir.org/bbs/showthr...&threadid=44083
Linux下的多進程編程
http://www.linuxsir.com/bbs/showthr...&threadid=48887
進程的建立
http://www.linuxsir.org/bbs/showthr...&threadid=44078
POSIX Threads Programming
http://www.llnl.gov/computing/tutor...reads/MAIN.html

linux系統調用
系統調用列表,編程必備
http://www-900.ibm.com/developerWor.../appendix.shtml
C語言庫函數---unix經常使用系統調用---使用說明
http://www.linuxsir.org/bbs/showthr...threadid=119136
http://chinaunix.net/forum/viewtopic.php?t=72159

FAQ列表
UNIX Programming FAQ 中文版,頗有參考價值
http://www.linuxforum.net/books/upfaq/book1.htm
UNIX Socket FAQ
http://www.developerweb.net/sock-faq/

編碼規範
Linux內核編程風格
http://www.linuxsir.org/bbs/showthr...=&postid=257594
GNU編碼標準
http://www.linuxforum.net/forum/sho...op&Number=29053

可執行文件格式與庫文件
從程序員角度看ELF
http://www.linuxsir.com/bbs/showthr...&threadid=48381
ELF可執行聯接規範(英漢對照版)
http://www.linuxaid.com.cn/articles...014528121.shtml
Linux動態連接庫高級應用
http://www.linuxsir.com/bbs/showthr...&threadid=18889
連接器與加載器,很是重要的一本書(Linkers and Loaders )
http://www.iecc.com/linker/

終端與串口編程、設備文件
UNIX下c語言的圖形編程--curses.h 函式庫
http://www.fanqiang.com/a4/b2/20020626/060200258.html
VT100控制碼
http://www.linuxsir.com/bbs/showthr...&threadid=43530
Linux ioctl() Primer
http://www.linuxsir.org/bbs/showthr...&threadid=44268
Linux Serial Programming How-to English version: http://www.sgmltools.org/HOWTO/Seri...g-HOWTO/t1.html
Linux Serial Programming How-to 中文繁體版:http://www.linux.org.tw/CLDP/OLD/Se...ming-HOWTO.html

linux中文化:

UTF-8 and Unicode FAQ
http://www.linuxforum.net/books/UTF-8-Unicode.html
unicode 如何轉換爲ASCII?
http://www.linuxforum.net/forum/gsh...=5&o=all&fpart=
一個臺灣的linux中文化站點
http://xcin.linux.org.tw/i18n/
UTF-8 and Unicode FAQ for Unix/Linux
http://www.cl.cam.ac.uk/~mgk25/unicode.html
Linux Unicode 編程
http://www-900.ibm.com/developerWor...uni/index.shtml
Linux 國際化本地化和中文化
http://www.linuxforum.net/doc/i18n-new.html

一些很好的編程資源鏈接:

HPCVL編程資源鏈接
http://www.hpcvl.org/faqs/prog_links.html
Linux Programming Resources
http://leapster.org/linoleum/

一個綜合的編程資源站點
http://www.clipx.net/norton.php

 


      Linux內核編碼風格 

   這篇簡短的文章描述了Linux內核首選的編碼風格。編碼風格是很我的化的東西,我不會把本身的觀點強加給任何人。可是,Linux內核的代碼畢竟是我必須有能力維護的,所以我寧願它的編碼風格是我喜歡的。請至少考慮一下這一點。 
   首先,我建議打印一份《GNU編碼標準》,不要閱讀它。燒掉它,它不過是象徵性的姿態。 
   而後,請看: 


            第 1 章: 縮進 

   Tabs(製表符)是8個字符的大小,所以縮進也應該是8個字符的大小。有些叛逆主張試圖把縮進變成4個(甚至是2個!)字符的長度,這就好象試圖把PI(案,圓周率)定義成3是同樣的。 
   依據:縮進背後的思想是:清楚地定義一個控制塊從哪裏開始,到哪裏結束。尤爲是在你接二連三的盯了20個小時的屏幕後,若是你有大尺寸的縮進。你將更容易發現縮進的好處。 
   如今,有些人說8個字符大小的縮進致使代碼太偏右了,而且在一個80字符寬的終端屏幕上看着很不舒服。對這個問題的回答是:若是你有超過3個級別的縮進,你就有點犯糊塗了,應當修改你的程序。 
   簡而言之,8個字符的縮進使程序更易讀,並且當你把功能隱藏的太深時,多層次的縮進還會對此很直觀的給出警告。要留心這種警告信息。 
    

            第 2 章: 放置花括號 

   C程序中另外一個要主意的就是花括號的放置。與縮進尺寸不一樣的是,關於如何放置花括號沒有技術上的理由。可是,首選的方法是象先知Brain Kernighan和Dennis Ritchie展示的那樣:把左括號放在行尾,右括號放在行首。也就是: 
    
   if (x is true) { 
      we do y 
   } 
    
   然而,還有另一種狀況,就是函數:函數應當把左右括號都放在行首。也就是: 
    
   int function(int x) 
   { 
      body of function 
   } 
    
   叛逆的人們所在皆有。他們說,這樣會致使…嗯,不一致性(案,指函數的花括號使用與其餘狀況不統一)。可是全部正確思考的人都知道:(1) K&R是正確的;(2) K&R仍是正確的。 並且,函數與別任何東西都不同(在C語言中你無法隱藏它)。 
   注意,右括號所在的行不該當有其它東西,除非跟隨着一個條件判斷。也就是do-while語句中的「while」和if-else語句中的「else」。象這樣: 
    
   do { 
      body of do-loop 
   } while (condition); 
    
和: 
    
   if (x == y) { 
      .. 
   } else if (x > y) { 
      ... 
   } else { 
      .... 
   } 
    
   依據: K&R。 
   並且,注意這種花括號的放置減小了空行的數目,並沒損害可讀性。所以,當屏幕上不能夠有不少空行時(試想25行的終端屏幕),你就有更多的空行來安插註釋。 
    

            第 3 章: 命名 

   C是一門樸素的語言,你使用的命名也應該這樣。與Modula-2和Pascal程序員不一樣,C程序員不使用諸如「ThisVariableIsATemporaryCounter」這樣「聰明」的名字。C程序員應該叫它「tmp」,這寫起來更簡單,也不會更難懂。 
   然而,當面對複雜狀況時就有些棘手,給全局變量取一個描述性的名字是必要的。把一個全局函數叫作「foo」是一種目光短淺的行爲。 
   全局變量(只當你確實須要時才用)應該有描述性的名字,全局函數也同樣。若是你有一個統計當前用戶個數的函數,應當把它命名爲「count_active_user()」或者簡單點些的相似名稱,不該該命名爲「cntusr()」。 
   把函數類型寫進函數名(即所謂的「匈牙利命名法」)簡直就是大腦有問題──編譯器老是知道函數的類型而且能加以檢查,這種命名法只會弄糊塗程序員本身。怪不得微軟老是製造充滿bug的程序。 
   局部變量的名字應該儘可能短,並且說到點子上。若是你有個普通的整型循環計數變量,應當命名爲「i」。命名爲「loop_counter」並不能帶來任何成效,若是它不被誤解的話(案,這裏的言外之意是說,若是被誤解就更慘了)。與此相似,「tmp」能夠做爲一個用來存儲任何類型臨時值的變量的名字。 
   若是你懼怕弄混淆局部變量(s)的名字,你就面臨着另外一個問題,也叫做「函數增加荷爾蒙失調綜合症」。請參考下一章。 
    

            第 4 章: 函數 

   函數應當短而精美,並且只作一件事。它們應當佔滿1或2個屏幕(就象咱們知道的那樣,ISO/ANSI的屏幕大小是80X24),只作一件事而且把它作好。 
   一個函數的最大長度與它的複雜度和縮進級別成反比。因此,若是若是你有一個概念上簡單(案,「簡單」是simple而不是easy)的函數,它偏偏包含着一個很長的case語句,這樣你不得不爲不一樣的狀況準備不懂的處理,那麼這樣的長函數是沒問題的。 
   然而,若是你有一個複雜的函數,你猜測一個並不是天才的高一學生可能看不懂得這個函數,你就應當努力把它減縮得更接近前面提到的最大函數長度限制。能夠使用一些輔助函數,給它們取描述性的名字(若是你認爲這些輔助函數的調用是性能關鍵的,可讓編譯器把它們內聯進來,這比在單個函數內完成全部的事情一般要好些)。 
   對函數還存在另外一個測量標準:局部變量的數目。這不應超過5到10個,不然你可能會弄錯。應當從新考慮這個函數,把它分解成小片。人類的大腦通常能同時記住7個不一樣的東西,超過這個數目就會犯糊塗。或許你認爲本身很聰明,那麼請你理解一下從如今開始的2周時間你都作什麼了。 

    
            第 5 章:註釋 

   註釋是有用的,但過量的註釋則是有害的。不要試圖在註釋中解釋你的代碼是如何工做的:把代碼是如何工做的視爲一件顯然的事情會更好些,並且,給糟糕的代碼做註釋則是在浪費時間。 

   一般,你願意本身的註釋說出代碼是作什麼的,而不是如何作。還有,儘可能避免在函數體內做註釋:若是函數很複雜,你極可能須要分開來註釋,回頭到第4章去看看吧。你能夠給一段代碼──漂亮的或醜陋的──做註釋以引發注意或警告,可是不要過量。取而代之,應當把註釋放在函數首部,告訴人們該函數做什麼,而不是爲何這樣作。 


            第 6 章:你把事情弄亂了 

   好吧,咱們來看看。極可能有長期使用UNIX的人告訴過你,「GNU emacs」能自動爲你格式化C程序源代碼,你注意到這是真的,它確實能作到,可是缺省狀況下它的用處遠遠小於指望值──鍵入無數的monkeys到GNU emacs中毫不可能造出好的程序。 
   所以,你能夠或者刪除GNU emacs,或者對它進行理智的配置。對於後者,能夠把下面的行粘貼到你的.emacs文件中: 
    
   (defun linux-c-mode () 
   "C mode with adjusted defaults for use with the Linux kernel." 
   (interactive) 
   (c-mode) 
   (c-set-style "K&R") 
   (setq c-basic-offset 8)) 
    
   這將會定義一個把C代碼弄成linux風格的命令。當hacking一個模塊時,若是你把「-*- linux-c -*-」放到了最初的兩行,這個模塊將被自動調用。並且,若是你打算每當在/usr/src/linux下編輯源文件時就自動調用它,也許你會把下面的命令: 
   (setq auto-mode-alist (cons '("/usr/src/linux.*/.*//.[ch]$" . linux-c-mode) 
            auto-mode-alist)) 
   添加進你的.emacs文件。 
   可是,即便你沒能讓emacs正確作到格式化,也並不是將就此一無全部:還有「indent」程序呢。 
   嗯,再提醒一下,GNU indent跟GNU emacs有一樣的毛病,這就須要你給它一些命令行選項。然而,這不是很糟糕的事,由於即便是GNU indent也認可K&R的權威性(GNU的人不是魔鬼,他們只是在這裏太過嚴格了,以至於誤導人),因此你能夠只需給indent這樣的選項:「-kr -i8」(表示「K&R風格,8個字符的縮進」)。 
   「indent」程序有不少選項,特別是當爲重排過的程序做註釋的時候,你須要看一下它的手冊。記住:「indent」可不是修正糟糕程序的萬能鑰匙。 
    

            第 7 章: 配置文件(configuration-files) 

   對配置選項來講(arch/xxx/config.in和全部的Config.in文件),使用不一樣的縮進風格。 
   若代碼中的縮進級別爲3,配置選項就應該爲2,這樣能夠暗示出依賴關係。後者只是用於bool/tristate(即二態/三態)的選項。對其它狀況用常識就好了。舉例來講: 

   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then 
      tristate 'Apply nitroglycerine inside the keyboard (DANGEROUS)' CONFIG_BOOM 
      if [ "$CONFIG_BOOM" != "n" ]; then 
         bool '  Output nice messages when you explode' CONFIG_CHEER 
      fi 
   fi 

   一般CONFIG_EXPERIMENTAL應當在全部不穩定的選項的周圍出現。全部已知會破壞數據的選項(如文件系統的實驗性的寫支持功能)應當被標記爲(DANGEROUS),其餘實驗性的選項應當被標記爲(EXPERIMENTAL)。 
    

            第 8 章: 數據結構 

   假如數據結構在其被建立/銷燬的線程環境(案:這裏說的線程是一個執行實體,多是進程、內核線程或其它)以外還具備可見性,那麼他們都該有引用計數。 在內核中沒有垃圾收集機制(並且內核以外的垃圾收集也是緩慢而低效的),這意味着你絕對應該爲每一次使用進行引用計數。 
   引用計數意味着你能夠避開鎖,還能容許多個線程並行訪問該數據結構──並且不用擔憂僅僅由於訪問數據結構的線程睡眠了一下子或者幹別的去了,它們就會消失。 
   注意,鎖不是引用計數的替代品。鎖是用來保持數據結構的一致性的,而引用計數是一種內存管理技術。一般兩者都須要,並且不會彼此混淆。 
   確實有許多數據結構能夠有兩個級別的引用計數,當使用者具備不一樣的「等級」(classes)時就是這樣。子等級(subclass)記錄了處於該等級的使用者個數,並且當它減到零的時候就把整體計數(global count)減一。 
   這種「多級引用計數」(multi-reference-counting)的一個實例能夠在內存管理子系統("struct mm_struct":mm_users和mm_count)中找到,也能夠在文件系統的代碼中("struct super_block":s_count和s_active)找到。 
   記住:若是另外一個線程能找到你的數據結構,而你有沒對它作引用計數,那幾乎能夠確定:這是一個bug。linux


 


 

 

曾經碰到過讓你疑惑不解、相似於int * (* (*fp1) (int) ) [10];這樣的變量聲明嗎?本文將由易到難,一步一步教會你如何理解這種複雜的C/C++聲明。ios

咱們將從天天都能碰到的較 
簡單的聲明入手,而後逐步加入const修飾符和typedef,還有函數指針,最後介紹一個可以讓你準確地理解任何C/C++聲明的「右左法則」。c++

須要強調一下的是,複雜的C/C++聲明並非好的編程風格;我這裏僅僅是教你如何去理解這些聲明。注意:爲了保證可以在同一行上顯示代碼和相關注釋,本文最好在至少1024x768分辨率的顯示器上閱讀。git

基礎程序員

讓咱們從一個很是簡單的例子開始,以下:web


   int n;算法


這個應該被理解爲「declare n as an int」(n是一個int型的變量)。shell

接下去來看一下指針變量,以下:


 int *p;

 

這個應該被理解爲「declare p as an int *」(p是一個int *型的變量),或者說p是一個指向一個int型變量的指針。我想在這裏展開討論一下:我以爲在聲明一個指針(或引用)類型的變量時,最好將*(或&)寫在緊靠變量以前,而不是緊跟基本類型以後。這樣能夠避免一些理解上的誤區,好比:


 int*   p,q;

 

第一眼看去,好像是p和q都是int*類型的,但事實上,只有p是一個指針,而q是一個最簡單的int型變量。

咱們仍是繼續咱們前面的話題,再來看一個指針的指針的例子:


 char **argv;


理論上,對於指針的級數沒有限制,你能夠定義一個浮點類型變量的指針的指針的指針的指針...

再來看以下的聲明:


 int RollNum[30][4];
int (*p)[4]=RollNum;
int *q[5];


這裏,p被聲明爲一個指向一個4元素(int類型)數組的指針,而q被聲明爲一個包含5個元素(int類型的指針)的數組。

另外,咱們還能夠在同一個聲明中混合實用*和&,以下:


 int **p1;  //  p1 is a pointer  ;  to a pointer   to an int.
int *&p2;  //  p2 is a reference to a pointer   to an int.
int &*p3;  //  ERROR: Pointer    to a reference is illegal.
int &&p4;  //  ERROR: Reference  to a reference is illegal.


注:p1是一個int類型的指針的指針;p2是一個int類型的指針的引用;p3是一個int類型引用的指針(不合法!);p4是一個int類型引用的引用(不合法!)。

const修飾符

  當你想阻止一個變量被改變,可能會用到const關鍵字。在你給一個變量加上const修飾符的同時,一般須要對它進行初始化,由於之後的任什麼時候候你將沒有機會再去改變它。例如:


 const int n=5;
int const m=10;


  上述兩個變量n和m實際上是同一種類型的——都是const int(整形恆量)。由於C++標準規定,const關鍵字放在類型或變量名以前等價的。我我的更喜歡第一種聲明方式,由於它更突出了const修飾符的做用。

  當const與指針一塊兒使用時,容易讓人感到迷惑。例如,咱們來看一下下面的p和q的聲明:


 const int *p;
int const *q;


  他們當中哪個表明const int類型的指針(const直接修飾int),哪個表明int類型的const指針(const直接修飾指針)?實際上,p和q都被聲明爲const int類型的指針。而int類型的const指針應該這樣聲明:


 int * const r= &n; // n has been declared as an int


  這裏,p和q都是指向const int類型的指針,也就是說,你在之後的程序裏不能改變*p的值。而r是一個const指針,它在聲明的時候被初始化指向變量n(即r=&n;)以後,r的值將再也不容許被改變(但*r的值能夠改變)。

  組合上述兩種const修飾的狀況,咱們來聲明一個指向const int類型的const指針,以下:


 const int * const p=&n // n has been declared as const int


  下面給出的一些關於const的聲明,將幫助你完全理清const的用法。不過請注意,下面的一些聲明是不能被編譯經過的,由於他們須要在聲明的同時進行初始化。爲了簡潔起見,我忽略了初始化部分;由於加入初始化代碼的話,下面每一個聲明都將增長兩行代碼。


 char ** p1;                    //        pointer to       pointer to       char
const char **p2;               //        pointer to       pointer to const char
char * const * p3;             //        pointer to const pointer to       char
const char * const * p4;       //        pointer to const pointer to const char
char ** const p5;              //  const pointer to       pointer to       char
const char ** const p6;        //  const pointer to       pointer to const char
char * const * const p7;       //  const pointer to const pointer to       char
const char * const * const p8; //  const pointer to const pointer to const char


 注:p1是指向char類型的指針的指針;p2是指向const char類型的指針的指針;p3是指向char類型的const指針;p4是指向const char類型的const指

針;p5是指向char類型的指針的const指針;p6是指向const char類型的指針的const指針;p7是指向char類型const指針的const指針;p8是指向const char類型的const指針的const指針。

typedef的妙用

  typedef給你一種方式來克服「*只適合於變量而不適合於類型」的弊端。你能夠以下使用typedef:


 typedef char * PCHAR;
PCHAR p,q;


  這裏的p和q都被聲明爲指針。(若是不使用typedef,q將被聲明爲一個char變量,這跟咱們的第一眼感受不太一致!)下面有一些使用typedef的聲明,而且給出瞭解釋:


 typedef char * a;  // a is a pointer to a char

typedef a b();     // b is a function that returns
                   // a pointer to a char

typedef b *c;      // c is a pointer to a function
                   // that returns a pointer to a char

typedef c d();     // d is a function returning
                   // a pointer to a function
                   // that returns a pointer to a char

typedef d *e;      // e is a pointer to a function 
                   // returning  a pointer to a 
                   // function that returns a 
                   // pointer to a char

e var[10];         // var is an array of 10 pointers to 
                   // functions returning pointers to 
                   // functions returning pointers to chars.
 


  typedef常常用在一個結構聲明以前,以下。這樣,當建立結構變量的時候,容許你不使用關鍵字struct(在C中,建立結構變量時要求使用struct關鍵字,如struct tagPOINT a;而在C++中,struct能夠忽略,如tagPOINT b)。


 typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

POINT p; /* Valid C code */
 

函數指針

  函數指針多是最容易引發理解上的困惑的聲明。函數指針在DOS時代寫TSR程序時用得最多;在Win32和X-Windows時代,他們被用在須要回調

函數的場合。固然,還有其它不少地方須要用到函數指針:虛函數表,STL中的一些模板,Win NT/2K/XP系統服務等。讓咱們來看一個函數指針的簡單例子:


 int (*p)(char);


  這裏p被聲明爲一個函數指針,這個函數帶一個char類型的參數,而且有一個int類型的返回值。另外,帶有兩個float類型參數、返回值是char類型的指針的指針的函數指針能夠聲明以下:


 char ** (*p)(float, float);


  那麼,帶兩個char類型的const指針參數、無返回值的函數指針又該如何聲明呢?參考以下:


 void * (*a[5])(char * const, char * const);


  「右左法則」[重要!!!]

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

  這是一個簡單的法則,但能讓你準確理解全部的聲明。這個法則運用以下:從最內部的括號開始閱讀聲明,向右看,而後向左看。當你碰到一個括號時就調轉閱讀的方向。括號內的全部內容都分析完畢就跳出括號的範圍。這樣繼續,直到整個聲明都被分析完畢。

  對上述「右左法則」作一個小小的修正:當你第一次開始閱讀聲明的時候,你必須從變量名開始,而不是從最內部的括號。

  下面結合例子來演示一下「右左法則」的使用。


 int * (* (*fp1) (int) ) [10];


  閱讀步驟:
  1. 從變量名開始 -------------------------------------------- fp1
  2. 往右看,什麼也沒有,碰到了),所以往左看,碰到一個* ------ 一個指針
  3. 跳出括號,碰到了(int) ----------------------------------- 一個帶一個int參數的函數
  4. 向左看,發現一個* --------------------------------------- (函數)返回一個指針
  5. 跳出括號,向右看,碰到[10] ------------------------------ 一個10元素的數組
  6. 向左看,發現一個* --------------------------------------- 指針
  7. 向左看,發現int ----------------------------------------- int類型

  總結:fp1被聲明成爲一個函數的指針的指針的數組,這個數組有10個元素,函數的原型爲帶一個int類型的參數,返回值爲一個指針?

再來看一個例子:

 int *( *( *arr[5])())();


  閱讀步驟:
  1. 從變量名開始 --------

------------------------------------ arr
  2. 往右看,發現是一個數組 ---------------------------------- 一個5元素的數組
  3. 向左看,發現一個* --------------------------------------- 指針
  4. 跳出括號,向右看,發現() -------------------------------- 不帶參數的函數
  5. 向左看,碰到* ------------------------------------------- (函數)返回一個指針
  6. 跳出括號,向右發現() ------------------------------------ 不帶參數的函數
  7. 向左,發現* --------------------------------------------- (函數)返回一個指針
  8. 繼續向左,發現int --------------------------------------- int類型

  總結:

  還有更多的例子:


 float ( * ( *b()) [] )();              // b is a function that returns a 
                                       // pointer to an array of pointers
                                       // to functions returning floats.

void * ( *c) ( char, int (*)());       // c is a pointer to a function that takes
                                       // two parameters:
                                       //     a char and a pointer to a
                                       //     function that takes no
                                       //     parameters and returns
                                       //     an int
                                       // and returns a pointer to void.

void ** (*d) (int &, 
  char **(*)(char *, char **));        // d is a pointer to a function that takes
                                       // two parameters:
                                       //     a reference to an int and a pointer
                                       //     to a function that takes two parameters:
                                       //        a pointer to a char and a pointer
                                       //        to a pointer to a char
                                       //     and returns a pointer to a pointer 
                                       //     to a char
                                       // and returns a pointer to a pointer to void

float ( * ( * e[10]) 
    (int &) ) [5];                    // e is an array of 10 pointers to 
                                       // functions that take a single
                                       // reference to an int as an argument 
                                       // and return pointers to
                                       // an array of 5 floats.
 

 


 


char * strcpy( char *strDest, const char *strSrc )  
{
    assert( (strDest != NULL) && (strSrc != NULL) );
    char *address = strDest;  
    while( (*strDest++ = * strSrc++) != ‘/0’ )
           ;
    return address;
}
注意的地方:
1,const char *strSrc  由於strSrc是輸入參數,不能被修改。
2,assert的使用。頭文件加上include<assert.h>。對源地址和目的地址加非0斷言,增強程序的健壯性。若是strDest 或者strSrc爲空指針,那麼系統會提示:Assertion failed,並指出出錯位置。
3,用了一個變量address。是爲了實現鏈式操做,將目的地址返回。好比strcpy( s,strcpy(t,a) );把a的內容複製給t,再把t的內容複製給s。
4,while( (*strDest++ = * strSrc++) != ‘/0’ )。根據優先級,先把strSrc所指內存的值賦值到strDest所指的內存中,而後strSrc指向下一塊內存,strDest也同樣。直到遇到'/0'爲止。此時,*strDest和*strSrc的值都爲'/0'。


我本身寫了一個程序,用來驗證這個函數。但卻犯了一個錯誤。後來看了一下本身之前的筆記才發現錯誤的緣由。貼出來,看你能看到錯誤在哪裏嗎?
#include<stdio.h>
#include<assert.h>

char *strcopy(char *strDest, const char *strSrc);

main()
{
 char *s = "moonbingbing";
 char *t = "wenming";
 
 strcopy(s,t);
 printf("%s/n%s/n",s,t);
 return 0;
}

char *strcopy(char *strDest, const char *strSrc)  
{
    assert( (strDest != NULL) && (strSrc != NULL) );
    char *address = strDest;  
    while( (*strDest++ = * strSrc++) != '/0' )
           ;
    return address;
}


錯就錯在char *s = "moonbingbing";char *t = "wenming";
由於在ANSI C中,指針p 指向的字符串是看成常量來處理的。若是你的編譯器是tc2.0 ,tc++3.0 ,wintc,那麼編譯器不會報錯。從這個程序能夠看出,寫一個程序不難,但寫好就容易了。這些庫函數都是高手們寫的,要考慮健壯性,安全性,擴展性。這就是高手和和通常人的區別了。
這些東西在林銳的書上見過,有一段時間沒溫習c語言了。看一遍就有一些新的收穫,溫故而知新啊。因此記下來,別再忘了。

 


 


 

scanf函數
我曾經在這個函數上犯過很多錯誤,也看到別人犯過的錯誤,記下來,提醒本身不要重蹈覆轍了。若是對你有用,那就更好了:)若是你發現文章中有錯誤,歡迎你不吝賜教。但願和你們一塊兒學習。

有關詳細的scanf函數解釋,你們能夠去看看《C程序設計語言》(K&C)和《C語言大全》(後面我把其中scanf的部分貼了出來)。


曾經錯的幾個地方:(xpsp2,vc6.0環境下)
1.空白符問題
#include<stdio.h>
main()
{   
 int a;
 printf("input the data/n");
 scanf("%d/n",&a);//這裏多了一個回車符/n
   printf("%d",a);
 return 0;
}
結果要輸入兩個數程序才結束,而不是預期的一個。why?
緣由:用空白符結尾時,scanf會跳過空白符去讀下一個字符,因此你必須再輸入一個數。這裏的空白符包括空格,製表符,換行符,回車符和換頁符。因此若是你用scanf("%d  ",&a)也會出現一樣的問題。
解決方法:這種錯誤大可能是輸入的時候不當心,多注意一點就行了。這種問題也很差檢查,編譯沒有問題,一個空格也不容易看出來。當你的程序出現上面的問題時,本身對照檢查一下就能夠了。


2.緩衝區問題
這是一個很是容易錯的地方,我就錯過屢次。
#include <stdio.h>
main()
{
 int n = 5;
 char c[n];
 for(int i = 0; i < n; i++)
  c[i] = scanf("%c",&c[i]); 
 printf(c);
return 0;
}
若是輸入:
a
b
c
那麼循環就會「提早」結束了.
緣由:輸入a和第一個回車後,a和這個回車符都留在緩衝區中。第一個scanf讀取了a,可是輸入緩衝區裏面還留有一個/n,第二個scanf讀取這個/n。而後輸入b和第二個回車,一樣的,第三個scanf讀取了b,第四個scanf讀取了第二個回車符。第五個讀取了c。因此五個scanf都執行了,並無提早結束。只不過有的scanf讀取到了回車符而已。
解決方法:把程序改爲這樣就能夠了:
for( i = 0; i < n; i++){
  scanf("%c",&c[i]);
fflush(stdin);//刷新緩衝區
}
或者不用scanf,而用gets()函數,如:
#include<stdio.h>
main()
{   
 char c[5];
 gets(c);
 printf(c);
 return 0;
}
但要注意:這個函數自動把你最後敲的回車轉換爲字符'/0'。若是你的輸入超過了數組的大小,那麼就會產生錯誤。


3.scanf()函數的參數輸入類型不匹配問題
這是我在csdn論壇上見到的問題,這個錯誤有時候會讓人莫名其妙。
#include<stdio.h>
main()
{
 int a=123;
 char c='t';
 printf("input/n");
 scanf("%d%c",&a,&c);
 scanf("%d%c",&a,&c);
 scanf("%d%c",&a,&c);
 printf("%d/n%c/n",a,c);
 return 0;
}
當輸入a 回車 後,會直接跳過下面2個scanf語句,直接輸出爲
123
t
緣由:對於scanf("%d%c",&a,&c),scanf語句執行時,首先試圖從緩衝區中讀入一個%d類型的數據,若是和第一個參數匹配,則繼續從緩衝區中讀取數據和第二個參數進行匹配,依次進行下去,直到匹配完全部的參數;若是其中有一個參數不匹配,那就從這個地方跳出,忽略這個scanf後面全部的參數,而去執行下一條語句。 
能夠用下面的程序驗證一下:
#include <stdio.h>
int main()
{
 int a=123,b=1;
 char c='t';
   scanf("%d%d",&a,&b);
 scanf("%c",&c);
 printf("%d/n%d/n%c/n",a,b,c);
 return 0;
}輸入:2 回車a 回車
結果是:
2
1
a
解決方法:scanf()函數執行成功時的返回值是成功讀取的變量數,也就是說,你這個scanf()函數有幾個變量,若是scanf()函數所有正常讀取,它就返回幾。但這裏還要注意另外一個問題,若是輸入了非法數據,鍵盤緩衝區就可能還個有殘餘信息問題。
好比:
#include <stdio.h>
 main()
{
 int a=123,b;
 while(scanf("%d%d",&a,&b)!=2)
  fflush(stdin);
 printf("%d/n%d/n",a,b);
 return 0;
}
你能夠試一下,若是輸入不是數字時,會有什麼反應。

補充:scanf中一種不多見但頗有用的轉換字符:[...]和[ ^...]。
#include<stdio.h>
 main() 

char strings[100]; 
scanf("%[1234567890]",strings); 
printf("%s",strings);
return  0; 

運行,輸入:1234werew後,結果是:1234。
經過運行能夠發現它的做用是:若是輸入的字符屬於方括號內字符串中某個字符,那麼就提取該字符;若是一經發現不屬於就結束提取。該方法會自動加上一個字符串結束符到已經提取的字符後面。 
scanf("%[^1234567890]",strings); 它的做用是:若是一經發現輸入的字符屬於方括號內字符串中某個字符,那麼就結束提取;若是不屬於就提取該字符。該方法會自動加上一個字符串結束符到已經提取的字符後面。 
注意:方括號兩邊不能空格,如:scanf("%[ 1234567890 ]",strings); scanf("%[ ^1234567890 ]",strings); 不讓空格也會算在裏面的。
用這種方法還能夠解決scanf的輸入中不能有空格的問題。只要用
scanf("%[^/n]",strings); 就能夠了。很神奇吧。

scanf原型:參見《C語言大全》和K&C
# include <stdio.h>;
int scanf( const char *format, ... );
 函數 scanf() 是從標準輸入流 stdin 中讀內容的通用子程序,能夠讀入所有固有類型的數據並自動轉換成機內形式。
    在 C99 中,format 用 restrict 修飾。
format 指向的控制串由如下三類字符組成:
       ● 格式說明符
       ● 空白符
       ● 非空白符
     
     轉換字符(就是%後跟的部分)              
       a   讀浮點值(僅適用於 C99)                                  
       A   讀浮點值(僅適用於 C99)                                                  
       c   讀單字符                                                
       d   讀十進制整數                                            
       i   讀十進制、八進制、十六進制整數                          
       e   讀浮點數                                                
       E   讀浮點數                                                
       f   讀浮點數                                                
       F   讀浮點數(僅適用於 C99)                                  
       g   讀浮點數                                                
       G   讀浮點數                                                
       o   讀八進制數                                              
       s   讀字符串                                                
       x   讀十六進制數                                            
       X   讀十六進制數                                            
       p   讀指針值                                                
       n   至此已讀入值的等價字符數                                
       u   讀無符號十進制整數                                      
      [ ]  掃描字符集合                                            
       %   讀 % 符號(百分號)                                       
    
    例如: %s 表示讀串而 %d 表示讀整數。格式串的處理順序爲從左到右,格式說明符逐一與變元表中的變元匹配。爲了讀取長整數,能夠將 l(ell) 放在格式說明符的前面;爲了讀取短整數,能夠將 h 放在格式說明符的前面。這些修飾符能夠與 d、i、o、u 和 x 格式代碼一塊兒使用。

    默認狀況下,a、f、e 和 g 告訴 scanf() 爲 float 分配數據。 若是將 l(ell) 放在這些修飾符的前面,則 scanf() 爲 double 分配數據。使用 L 就是告訴 scanf(),接收數據的變量是 long double 型變量。

    若是使用的現代編譯器程序支持 1995 年增長的寬字符特性, 則能夠與 c 格式代碼一塊兒,用 l 修飾符說明類型 wchar_t 的寬字符指針;也能夠與 s 格式代碼一塊兒,用 l 修飾符說明寬字符串的指針。l 修飾符也能夠用於修飾掃描集,以說明寬字符。

    控制串中的空白符使 scanf() 在輸入流中跳過一個或多個空白行。空白符能夠是空格(space)、製表符(tab)和新行符(newline)。 本質上,控制串中的空白符使 scanf() 在輸入流中讀,但不保存結果,直到發現非空白字符爲止。

    非空白符使 scanf() 在流中讀一個匹配的字符並忽略之。例如,"%d,%d" 使 scanf() 先讀入一個整數,讀入中放棄逗號,而後讀另外一個整數。如未發現匹配,scanf() 返回。

    scanf() 中用於保存讀入值的變元必須都是變量指針,即相應變量的地址。

    在輸入流中,數據項必須由空格、製表符和新行符分割。逗號和分號等不是分隔符,好比如下代碼:
    scanf( "%d %d", &r, &c );
將接受輸入 10 20,但遇到 10,20 則失敗。

    百分號(%)與格式符之間的星號(*)表示讀指定類型的數據但不保存。所以,
    scanf( "%d %*c %d", &x, &y );
對 10/20 的讀入操做中,10 放入變量 x,20 放入 y。

    格式命令能夠說明最大域寬。 在百分號(%)與格式碼之間的整數用於限制從對應域讀入的最大字符數。例如,但願向 address 讀入很少於 20 個字符時,能夠書寫成以下形式:
    scanf( "%20s", address );

    若是輸入流的內容多於 20 個字符,則下次 scanf() 今後次中止處開始讀入。 若達到最大域寬前已遇到空白符,則對該域的讀當即中止;此時,scanf() 跳到下一個域。

    雖然空格、製表符和新行符都用作域分割符號,但讀單字符操做中卻按通常字符處理。例如,對輸入流 "x y" 調用:
    scanf( "%c%c%c", &a, &b, &c );
返回後,x 在變量 a 中,空格在變量 b 中,y 在變量 c 中。

    注意,控制串中的其它字符,包括空格、製表符和新行符,都用於從輸入流中匹配並放棄字符,被匹配的字符都放棄。例如,給定輸入流 "10t20",調用:
    scanf( "%dt%d", &x, &y );
將把 10 和 20 分別放到 x 和 y 中,t 被放棄,由於 t 在控制串中。

    ANSI C 標準向 scanf() 增長了一種新特性,稱爲掃描集(scanset)。 掃描集定義一個字符集合,可由 scanf() 讀入其中容許的字符並賦給對應字符數組。 掃描集合由一對方括號中的一串字符定義,左方括號前必須綴以百分號。 例如,如下的掃描集使 scanf() 讀入字符 A、B 和 C:
    %[ABC]

    使用掃描集時,scanf() 連續吃進集合中的字符並放入對應的字符數組,直到發現不在集合中的字符爲止(即掃描集僅讀匹配的字符)。返回時,數組中放置以 null 結尾、由讀入字符組成的字符串。

    用字符 ^ 能夠說明補集。把 ^ 字符放爲掃描集的第一字符時,構成其它字符組成的命令的補集合,指示 scanf() 只接受未說明的其它字符。
    對於許多實現來講,用連字符能夠說明一個範圍。 例如,如下掃描集使 scanf() 接受字母 A 到 Z:
    %[A-Z]
    重要的是要注意掃描集是區分大小寫的。所以,但願掃描大、小寫字符時,應該分別說明大、小寫字母。
    scanf() 返回等於成功賦值的域數的值,但因爲星號修飾符而讀入未賦值的域不計算在內。給第一個域賦值前已出錯時,返回 EOF。

    C99 爲 scanf() 增長了幾個格式修飾符:hh、ll、j、z 和 t。hh 修飾符可用於 d、i、o、u、x、X 或 n。它說明相應的變元是 signed 或 unsigned char 值,或用於 n 時, 相應的變元是指向 long char 型變量的指針。ll 修飾符也可用於 d、i、o、u、x、X 或 n。它說明相應的變元是 signed 或者 unsigned long long int 值。
    j 格式修飾符應用於 d、i、o、u、x、X 或 n,說明匹配的變元是類型 intmax_t 或 uintmax_t。這些類型在 <stdint.h>; 中聲明,並說明最大寬度的整數。
    z 格式修飾符應用於 d、i、o、u、x、X 或 n,說明匹配的變元是指向 size_t 類型對象的指針。該類型在 <stddef.h>; 中聲明,並說明 sizeof 的結構。
    t 格式修飾符應用於 d、i、o、u、x、X 或 n,說明匹配的變元是指向 ptrdiff_t  類型對象的指針。該類型在 <stddef.h>; 中聲明,並說明兩個指針之間的差異。

例子:

# include <stdio.h>;
int main( void )
{
    char str[80], str2[80];
    int i;
 /* read a string and a integer */
    scanf( "%s%d", str, &i );
 /* read up to 79 chars into str */
    scanf( "%79s", str );
 /* skip the integer between the two strings */
    scanf( "%s%*d%s", str, str2 );
  return 0;
}

 


 


分類函數,所在函數庫爲ctype.h 
int isalpha(int ch) 若ch是字母('A'-'Z','a'-'z')返回非0值,不然返回0 
int isalnum(int ch) 若ch是字母('A'-'Z','a'-'z')或數字('0'-'9'),返回非0值,不然返回0 
int isascii(int ch) 若ch是字符(ASCII碼中的0-127)返回非0值,不然返回0 
int iscntrl(int ch) 若ch是做廢字符(0x7F)或普通控制字符(0x00-0x1F),返回非0值,不然返回0 
int isdigit(int ch) 若ch是數字('0'-'9')返回非0值,不然返回0 
int isgraph(int ch) 若ch是可打印字符(不含空格)(0x21-0x7E)返回非0值,不然返回0 
int islower(int ch) 若ch是小寫字母('a'-'z')返回非0值,不然返回0 
int isprint(int ch) 若ch是可打印字符(含空格)(0x20-0x7E)返回非0值,不然返回0 
int ispunct(int ch) 若ch是標點字符(0x00-0x1F)返回非0值,不然返回0 
int isspace(int ch) 若ch是空格(' '),水平製表符('/t'),回車符('/r'), 走紙換行('/f'),垂直製表符('/v'),換行符('/n'), 返回非0值,不然返回0 
int isupper(int ch) 若ch是大寫字母('A'-'Z')返回非0值,不然返回0 
int isxdigit(int ch) 若ch是16進制數('0'-'9','A'-'F','a'-'f')返回非0值, 不然返回0 
int tolower(int ch) 若ch是大寫字母('A'-'Z')返回相應的小寫字母('a'-'z') 
int toupper(int ch) 若ch是小寫字母('a'-'z')返回相應的大寫字母('A'-'Z')

數學函數,所在函數庫爲math.h、stdlib.h、string.h、float.h 
int abs(int i) 返回整型參數i的絕對值 
double cabs(struct complex znum) 返回複數znum的絕對值 
double fabs(double x) 返回雙精度參數x的絕對值 
long labs(long n) 返回長整型參數n的絕對值 
double exp(double x) 返回指數函數ex的值 
double frexp(double value,int *eptr) 返回value=x*2n中x的值,n存貯在eptr中 
double ldexp(double value,int exp); 返回value*2exp的值 
double log(double x) 返回logex的值 
double log10(double x) 返回log10x的值 
double pow(double x,double y) 返回xy的值 
double pow10(int p) 返回10p的值 
double sqrt(double x) 返回x的開方 
double acos(double x) 返回x的反餘弦cos-1(x)值,x爲弧度 
double asin(double x) 返回x的反正弦sin-1(x)值,x爲弧度 
double atan(double x) 返回x的反正切tan-1(x)值,x爲弧度 
double atan2(double y,double x) 返回y/x的反正切tan-1(x)值,y的x爲弧度 
double cos(double x) 返回x的餘弦cos(x)值,x爲弧度 
double sin(double x) 返回x的正弦sin(x)值,x爲弧度 
double tan(double x) 返回x的正切tan(x)值,x爲弧度 
double cosh(double x) 返回x的雙曲餘弦cosh(x)值,x爲弧度 
double sinh(double x) 返回x的雙曲正弦sinh(x)值,x爲弧度 
double tanh(double x) 返回x的雙曲正切tanh(x)值,x爲弧度 
double hypot(double x,double y) 返回直角三角形斜邊的長度(z), x和y爲直角邊的長度,z2=x2+y2 
double ceil(double x) 返回不小於x的最小整數 
double floor(double x) 返回不大於x的最大整數 
void srand(unsigned seed) 初始化隨機數發生器 
int rand() 產生一個隨機數並返回這個數 
double poly(double x,int n,double c[]) 從參數產生一個多項式 
double modf(double value,double *iptr) 將雙精度數value分解成尾數和階 
double fmod(double x,double y) 返回x/y的餘數 
double frexp(double value,int *eptr) 將雙精度數value分紅尾數和階 
double atof(char *nptr) 將字符串nptr轉換成浮點數並返回這個浮點數 
double atoi(char *nptr) 將字符串nptr轉換成整數並返回這個整數 
double atol(char *nptr) 將字符串nptr轉換成長整數並返回這個整數 
char *ecvt(double value,int ndigit,int *decpt,int *sign) 
將浮點數value轉換成字符串並返回該字符串 
char *fcvt(double value,int ndigit,int *decpt,int *sign) 
將浮點數value轉換成字符串並返回該字符串 
char *gcvt(double value,int ndigit,char *buf) 
將數value轉換成字符串並存於buf中,並返回buf的指針 
char *ultoa(unsigned long value,char *string,int radix) 
將無符號整型數value轉換成字符串並返回該字符串,radix爲轉換時所用基數 
char *ltoa(long value,char *string,int radix) 
將長整型數value轉換成字符串並返回該字符串,radix爲轉換時所用基數 
char *itoa(int value,char *string,int radix) 
將整數value轉換成字符串存入string,radix爲轉換時所用基數 
double atof(char *nptr) 將字符串nptr轉換成雙精度數,並返回這個數,錯誤返回0 
int atoi(char *nptr) 將字符串nptr轉換成整型數, 並返回這個數,錯誤返回0 
long atol(char *nptr) 將字符串nptr轉換成長整型數,並返回這個數,錯誤返回0 
double strtod(char *str,char **endptr)將字符串str轉換成雙精度數,並返回這個數, 
long strtol(char *str,char **endptr,int base)將字符串str轉換成長整型數, 並返回這個數, 
int matherr(struct exception *e) 用戶修改數學錯誤返回信息函數(沒有必要使用) 
double _matherr(_mexcep why,char *fun,double *arg1p, double *arg2p,double retval) 
用戶修改數學錯誤返回信息函數(沒有必要使用) 
unsigned int _clear87() 清除浮點狀態字並返回原來的浮點狀態 
void _fpreset() 從新初使化浮點數學程序包 
unsigned int _status87() 返回浮點狀態字

目錄函數,所在函數庫爲dir.h、dos.h 
int chdir(char *path) 使指定的目錄path(如:"C://WPS")變成當前的工做目錄,成功返回0 
int findfirst(char *pathname,struct ffblk *ffblk,int attrib) 
查找指定的文件,成功返回0 
pathname爲指定的目錄名和文件名,如"C://WPS//TXT" 
ffblk爲指定的保存文件信息的一個結構,定義以下: 
┏━━━━━━━━━━━━━━━━━━┓ 
┃struct ffblk ┃ 
┃{ ┃ 
┃ char ff_reserved[21]; /*DOS保留字*/┃ 
┃ char ff_attrib; /*文件屬性*/ ┃ 
┃ int ff_ftime; /*文件時間*/ ┃ 
┃ int ff_fdate; /*文件日期*/ ┃ 
┃ long ff_fsize; /*文件長度*/ ┃ 
┃ char ff_name[13]; /*文件名*/ ┃ 
┃} ┃ 
┗━━━━━━━━━━━━━━━━━━┛ 
attrib爲文件屬性,由如下字符表明 
┏━━━━━━━━━┳━━━━━━━━┓ 
┃FA_RDONLY 只讀文件┃FA_LABEL 卷標號┃ 
┃FA_HIDDEN 隱藏文件┃FA_DIREC 目錄 ┃ 
┃FA_SYSTEM 系統文件┃FA_ARCH 檔案 ┃ 
┗━━━━━━━━━┻━━━━━━━━┛ 
例: 
struct ffblk ff; 
findfirst("*.wps",&ff,FA_RDONLY);

int findnext(struct ffblk *ffblk) 取匹配finddirst的文件,成功返回0 
void fumerge(char *path,char *drive,char *dir,char *name,char *ext) 
此函數經過盤符drive(C:、A:等), 路徑dir(/TC、/BC/LIB等), 文件名name(TC、WPS等),擴展名ext(.EXE、.COM等)組成一個文件名存與path中. 
int fnsplit(char *path,char *drive,char *dir,char *name,char *ext) 
此函數將文件名path分解成盤符drive(C:、A:等), 路徑dir(/TC、/BC/LIB等), 文件名name(TC、WPS等),擴展名ext(.EXE、.COM等),並分別存入相應的變量中. 
int getcurdir(int drive,char *direc) 
此函數返回指定驅動器的當前工做目錄名稱。成功返回0 
drive 指定的驅動器(0=當前,1=A,2=B,3=C等) 
direc 保存指定驅動器當前工做路徑的變量 
char *getcwd(char *buf,iint n) 此函數取當前工做目錄並存入buf中,直到n個字節長爲爲止.錯誤返回NULL 
int getdisk() 取當前正在使用的驅動器,返回一個整數(0=A,1=B,2=C等) 
int setdisk(int drive) 設置要使用的驅動器drive(0=A,1=B,2=C等), 返回可以使用驅動器總數 
int mkdir(char *pathname) 創建一個新的目錄pathname,成功返回0 
int rmdir(char *pathname) 刪除一個目錄pathname,成功返回0 
char *mktemp(char *template) 構造一個當前目錄上沒有的文件名並存於template中 
char *searchpath(char *pathname) 利用MSDOS找出文件filename所在路徑, 此函數使用DOS的PATH變量,未找到文件返回NULL

進程函數,所在函數庫爲stdlib.h、process.h 
void abort() 此函數經過調用具備出口代碼3的_exit寫一個終止信息於stderr,並異常終止程序。無返回值

int exec…裝入和運行其它程序 
int execl(char *pathname,char *arg0,char *arg1,…,char *argn,NULL) 
int execle(char *pathname,char *arg0,char *arg1,…, char *argn,NULL,char *envp[]) 
int execlp(char *pathname,char *arg0,char *arg1,…,NULL) 
int execlpe(char *pathname,char *arg0,char *arg1,…,NULL,char *envp[]) 
int execv(char *pathname,char *argv[]) 
int execve(char *pathname,char *argv[],char *envp[]) 
int execvp(char *pathname,char *argv[]) 
int execvpe(char *pathname,char *argv[],char *envp[])

exec函數族裝入並運行程序pathname,並將參數arg0(arg1,arg2,argv[],envp[])傳遞給子程序,出錯返回-1。 
在exec函數族中,後綴l、v、p、e添加到exec後,所指定的函數將具備某種操做能力。 
有後綴 p時,函數能夠利用DOS的PATH變量查找子程序文件。 
l時,函數中被傳遞的參數個數固定。 
v時,函數中被傳遞的參數個數不固定。 
e時,函數傳遞指定參數envp,容許改變子進程的環境, 
無後綴 e時,子進程使用當前程序的環境。

void _exit(int status) 終止當前程序,但不清理現場 
void exit(int status) 終止當前程序,關閉全部文件,寫緩衝區的輸出(等待輸出), 並調用任何寄存器的"出口函數",無返回值

int spawn…運行子程序 
int spawnl(int mode,char *pathname,char *arg0,char *arg1,…, char *argn,NULL) 
int spawnle(int mode,char *pathname,char *arg0,char *arg1,…, char *argn,NULL,char *envp[]) 
int spawnlp(int mode,char *pathname,char *arg0,char *arg1,…, char *argn,NULL) 
int spawnlpe(int mode,char *pathname,char *arg0,char *arg1,…, char *argn,NULL,char *envp[]) 
int spawnv(int mode,char *pathname,char *argv[]) 
int spawnve(int mode,char *pathname,char *argv[],char *envp[]) 
int spawnvp(int mode,char *pathname,char *argv[]) 
int spawnvpe(int mode,char *pathname,char *argv[],char *envp[])

spawn函數族在mode模式下運行子程序pathname,並將參數arg0(arg1,arg2,argv[],envp[])傳遞給子程序.出錯返回-1 
mode爲運行模式: 
mode爲 P_WAIT 表示在子程序運行完後返回本程序 
P_NOWAIT 表示在子程序運行時同時運行本程序(不可用) 
P_OVERLAY 表示在本程序退出後運行子程序

在spawn函數族中,後綴l、v、p、e添加到spawn後,所指定的函數將具備某種操做能力 
有後綴 p時, 函數利用DOS的PATH查找子程序文件 
l時, 函數傳遞的參數個數固定. 
v時, 函數傳遞的參數個數不固定. 
e時, 指定參數envp能夠傳遞給子程序,容許改變子程序運行環境. 
無後綴 e時,子程序使用本程序的環境.

int system(char *command) 
將MSDOS命令command傳遞給DOS執行轉換子程序,函數庫爲math.h、stdlib.h、ctype.h、float.h 
char *ecvt(double value,int ndigit,int *decpt,int *sign) 
將浮點數value轉換成字符串並返回該字符串 
char *fcvt(double value,int ndigit,int *decpt,int *sign) 
將浮點數value轉換成字符串並返回該字符串 
char *gcvt(double value,int ndigit,char *buf) 
將數value轉換成字符串並存於buf中,並返回buf的指針 
char *ultoa(unsigned long value,char *string,int radix) 
將無符號整型數value轉換成字符串並返回該字符串,radix爲轉換時所用基數 
char *ltoa(long value,char *string,int radix) 
將長整型數value轉換成字符串並返回該字符串,radix爲轉換時所用基數 
char *itoa(int value,char *string,int radix) 
將整數value轉換成字符串存入string,radix爲轉換時所用基數 
double atof(char *nptr) 將字符串nptr轉換成雙精度數,並返回這個數,錯誤返回0 
int atoi(char *nptr) 將字符串nptr轉換成整型數, 並返回這個數,錯誤返回0 
long atol(char *nptr) 將字符串nptr轉換成長整型數,並返回這個數,錯誤返回0 
double strtod(char *str,char **endptr) 
將字符串str轉換成雙精度數,並返回這個數, 
long strtol(char *str,char **endptr,int base) 
將字符串str轉換成長整型數, 並返回這個數, 
int toascii(int c) 返回c相應的ASCII 
int tolower(int ch) 若ch是大寫字母('A'-'Z')返回相應的小寫字母('a'- 'z') 
int _tolower(int ch) 返回ch相應的小寫字母('a'-'z') 
int toupper(int ch) 若ch是小寫字母('a'-'z')返回相應的大寫字母('A'- 'Z') 
int _toupper(int ch) 返回ch相應的大寫字母('A'-'Z')

診斷函數,所在函數庫爲assert.h、math.h 
void assert(int test) 一個擴展成if語句那樣的宏,若是test測試失敗,就顯示一個信息並異常終止程序,無返回值 
void perror(char *string) 本函數將顯示最近一次的錯誤信息,格式如:字符串string:錯誤信息 
char *strerror(char *str) 本函數返回最近一次的錯誤信息,格式如: 字符串str:錯誤信息 
int matherr(struct exception *e) 
用戶修改數學錯誤返回信息函數(沒有必要使用) 
double _matherr(_mexcep why,char *fun,double *arg1p, double *arg2p,double retval) 
用戶修改數學錯誤返回信息函數(沒有必要使用)

輸入輸出子程序, 函數庫爲io.h、conio.h、stat.h、dos.h、stdio.h、signal.h

int kbhit() 本函數返回最近所敲的按鍵 
int fgetchar() 從控制檯(鍵盤)讀一個字符,顯示在屏幕上 
int getch() 從控制檯(鍵盤)讀一個字符,不顯示在屏幕上 
int putch() 向控制檯(鍵盤)寫一個字符 
int getchar() 從控制檯(鍵盤)讀一個字符,顯示在屏幕上 
int putchar() 向控制檯(鍵盤)寫一個字符 
int getche() 從控制檯(鍵盤)讀一個字符,顯示在屏幕上 
int ungetch(int c) 把字符c退回給控制檯(鍵盤) 
char *cgets(char *string) 從控制檯(鍵盤)讀入字符串存於string中 
int scanf(char *format[,argument…]) 
從控制檯讀入一個字符串,分別對各個參數進行賦值,使用BIOS進行輸出 
int vscanf(char *format,Valist param) 
從控制檯讀入一個字符串,分別對各個參數進行賦值,使用BIOS進行輸出,參數從Valist param中取得 
int cscanf(char *format[,argument…]) 
從控制檯讀入一個字符串,分別對各個參數進行賦值,直接對控制檯做操做,好比顯示器在顯示時字符時即爲直接寫頻方式顯示 
int sscanf(char *string,char *format[,argument,…]) 
經過字符串string, 分別對各個參數進行賦值 
int vsscanf(char *string,char *format,Vlist param) 
經過字符串string,分別對各個參數進行賦值,參數從Vlist param中取得 
int puts(char *string) 發關一個字符串string給控制檯(顯示器), 使用BIOS進行輸出 
void cputs(char *string) 發送一個字符串string給控制檯(顯示器), 直接對控制檯做操做,好比顯示器即爲直接寫頻方式顯示 
int printf(char *format[,argument,…]) 
發送格式化字符串輸出給控制檯(顯示器),使用BIOS進行輸出 
int vprintf(char *format,Valist param) 
發送格式化字符串輸出給控制檯(顯示器),使用BIOS進行輸出,參數從Valist param中取得 
int cprintf(char *format[,argument,…]) 
發送格式化字符串輸出給控制檯(顯示器), 直接對控制檯做操做,好比顯示器即爲直接寫頻方式顯示 
int vcprintf(char *format,Valist param) 
發送格式化字符串輸出給控制檯(顯示器), 直接對控制檯做操做,好比顯示器即爲直接寫頻方式顯示, 參數從Valist param中取得 
int sprintf(char *string,char *format[,argument,…]) 
將字符串string的內容從新寫爲格式化後的字符串 
int vsprintf(char *string,char *format,Valist param) 
將字符串string的內容從新寫爲格式化後的字符串,參數從Valist param中取得 
int rename(char *oldname,char *newname)將文件oldname的名稱改成newname 
int ioctl(int handle,int cmd[,int *argdx,int argcx]) 
本函數是用來控制輸入/輸出設備的,請見下表: 
┌───┬────────────────────────────┐ 
│cmd值 │功能 │ 
├───┼────────────────────────────┤ 
│ 0 │取出設備信息 │ 
│ 1 │設置設備信息 │ 
│ 2 │把argcx字節讀入由argdx所指的地址 │ 
│ 3 │在argdx所指的地址寫argcx字節 │ 
│ 4 │除把handle看成設備號(0=當前,1=A,等)以外,均和cmd=2時同樣 │ 
│ 5 │除把handle看成設備號(0=當前,1=A,等)以外,均和cmd=3時同樣 │ 
│ 6 │取輸入狀態 │ 
│ 7 │取輸出狀態 │ 
│ 8 │測試可換性;只對於DOS 3.x │ 
│ 11 │置分享衝突的重算計數;只對DOS 3.x │ 
└───┴────────────────────────────┘ 
int (*ssignal(int sig,int(*action)())() 執行軟件信號(不必使用) 
int gsignal(int sig) 執行軟件信號(不必使用)

int _open(char *pathname,int access)爲讀或寫打開一個文件, 按後按access來肯定是讀文件仍是寫文件,access值見下表 
┌──────┬────────────────────┐ 
│access值 │意義 │ 
├──────┼────────────────────┤ 
│O_RDONLY │讀文件 │ 
│O_WRONLY │寫文件 │ 
│O_RDWR │即讀也寫 │ 
│O_NOINHERIT │若文件沒有傳遞給子程序,則被包含 │ 
│O_DENYALL │只容許當前處理必須存取的文件 │ 
│O_DENYWRITE │只容許從任何其它打開的文件讀 │ 
│O_DENYREAD │只容許從任何其它打開的文件寫 │ 
│O_DENYNONE │容許其它共享打開的文件 │ 
└──────┴────────────────────┘ 
int open(char *pathname,int access[,int permiss])爲讀或寫打開一個文件, 按後按access來肯定是讀文件仍是寫文件,access值見下表 
┌────┬────────────────────┐ 
│access值│意義 │ 
├────┼────────────────────┤ 
│O_RDONLY│讀文件 │ 
│O_WRONLY│寫文件 │ 
│O_RDWR │即讀也寫 │ 
│O_NDELAY│沒有使用;對UNIX系統兼容 │ 
│O_APPEND│即讀也寫,但每次寫老是在文件尾添加 │ 
│O_CREAT │若文件存在,此標誌無用;若不存在,建新文件 │ 
│O_TRUNC │若文件存在,則長度被截爲0,屬性不變 │ 
│O_EXCL │未用;對UNIX系統兼容 │ 
│O_BINARY│此標誌可顯示地給出以二進制方式打開文件 │ 
│O_TEXT │此標誌可用於顯示地給出以文本方式打開文件│ 
└────┴────────────────────┘ 
permiss爲文件屬性,可爲如下值: 
S_IWRITE容許寫 S_IREAD容許讀 S_IREAD|S_IWRITE容許讀、寫 
int creat(char *filename,int permiss) 創建一個新文件filename,並設定讀寫性。 
permiss爲文件讀寫性,能夠爲如下值 
S_IWRITE容許寫 S_IREAD容許讀 S_IREAD|S_IWRITE容許讀、寫 
int _creat(char *filename,int attrib) 創建一個新文件filename,並設定文件屬性。 
attrib爲文件屬性,能夠爲如下值 
FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統 
int creatnew(char *filenamt,int attrib) 創建一個新文件filename,並設定文件屬性。 
attrib爲文件屬性,能夠爲如下值 
FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統 
int creattemp(char *filenamt,int attrib) 創建一個新文件filename,並設定文件屬性。 
attrib爲文件屬性,能夠爲如下值 
FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統 
int read(int handle,void *buf,int nbyte) 從文件號爲handle的文件中讀nbyte個字符存入buf中 
int _read(int handle,void *buf,int nbyte) 從文件號爲handle的文件中讀nbyte個字符存入buf中,直接調用MSDOS進行操做. 
int write(int handle,void *buf,int nbyte) 將buf中的nbyte個字符寫入文件號爲handle的文件中 
int _write(int handle,void *buf,int nbyte) 將buf中的nbyte個字符寫入文件號爲handle的文件中 
int dup(int handle) 複製一個文件處理指針handle,返回這個指針 
int dup2(int handle,int newhandle) 複製一個文件處理指針handle到newhandle 
int eof(int *handle) 檢查文件是否結束,結束返回1,不然返回0 
long filelength(int handle) 返回文件長度,handle爲文件號 
int setmode(int handle,unsigned mode)本函數用來設定文件號爲handle的文件的打開方式 
int getftime(int handle,struct ftime *ftime) 
讀取文件號爲handle的文件的時間,並將文件時間存於ftime結構中,成功返回0, ftime結構以下: 
┌─────────────────┐ 
│struct ftime │ 
│{ │ 
│ unsigned ft_tsec:5; /*秒*/ │ 
│ unsigned ft_min:6; /*分*/ │ 
│ unsigned ft_hour:5; /*時*/ │ 
│ unsigned ft_day:5; /*日*/ │ 
│ unsigned ft_month:4;/*月*/ │ 
│ unsigned ft_year:1; /*年-1980*/ │ 
│} │ 
└─────────────────┘ 
int setftime(int handle,struct ftime *ftime) 重寫文件號爲handle的文件時間, 
新時間在結構ftime中.成功返回0.結構ftime以下: 
┌─────────────────┐ 
│struct ftime │ 
│{ │ 
│ unsigned ft_tsec:5; /*秒*/ │ 
│ unsigned ft_min:6; /*分*/ │ 
│ unsigned ft_hour:5; /*時*/ │ 
│ unsigned ft_day:5; /*日*/ │ 
│ unsigned ft_month:4;/*月*/ │ 
│ unsigned ft_year:1; /*年-1980*/ │ 
│} │ 
└─────────────────┘ 
long lseek(int handle,long offset,int fromwhere) 
本函數將文件號爲handle的文件的指針移到fromwhere後的第offset個字節處. 
SEEK_SET文件開關 SEEK_CUR當前位置 SEEK_END文件尾 
long tell(int handle) 本函數返回文件號爲handle的文件指針,以字節表示 
int isatty(int handle)本函數用來取設備handle的類型 
int lock(int handle,long offset,long length) 對文件共享做封鎖 
int unlock(int handle,long offset,long length) 打開對文件共享的封鎖

int close(int handle) 關閉handle所表示的文件處理,handle是從_creat、creat、 
creatnew、creattemp、dup、dup二、_open、open中的一個處調用得到的文件處理 
成功返回0不然返回-1,可用於UNIX系統 
int _close(int handle) 關閉handle所表示的文件處理,handle是從_creat、creat、 
creatnew、creattemp、dup、dup二、_open、open中的一個處調用得到的文件處理 
成功返回0不然返回-1,只能用於MSDOS系統

FILE *fopen(char *filename,char *type) 打開一個文件filename,打開方式爲type, 
並返回這個文件指針,type可爲如下字符串加上後綴 
┌──┬────┬───────┬────────┐ 
│type│讀寫性 │文本/2進制文件│建新/打開舊文件 │ 
├──┼────┼───────┼────────┤ 
│r │讀 │文本 │打開舊的文件 │ 
│w │寫 │文本 │建新文件 │ 
│a │添加 │文本 │有就打開無則建新│ 
│r+ │讀/寫 │不限制 │打開 │ 
│w+ │讀/寫 │不限制 │建新文件 │ 
│a+ │讀/添加 │不限制 │有就打開無則建新│ 
└──┴────┴───────┴────────┘ 
可加的後綴爲t、b。加b表示文件以二進制形式進行操做,t不必使用 
例: ┌──────────────────┐ 
│#include<stdio.h> │ 
│main() │ 
│{ │ 
│ FILE *fp; │ 
│ fp=fopen("C://WPS//WPS.EXE","r+b");│ 
└──────────────────┘ 
FILE *fdopen(int ahndle,char *type) 
FILE *freopen(char *filename,char *type,FILE *stream) 
int getc(FILE *stream) 從流stream中讀一個字符,並返回這個字符 
int putc(int ch,FILE *stream) 向流stream寫入一個字符ch 
int getw(FILE *stream) 從流stream讀入一個整數,錯誤返回EOF 
int putw(int w,FILE *stream) 向流stream寫入一個整數 
int ungetc(char c,FILE *stream) 把字符c退回給流stream,下一次讀進的字符將是c 
int fgetc(FILE *stream) 從流stream處讀一個字符,並返回這個字符 
int fputc(int ch,FILE *stream) 將字符ch寫入流stream中 
char *fgets(char *string,int n,FILE *stream) 
從流stream中讀n個字符存入string中 
int fputs(char *string,FILE *stream)將字符串string寫入流stream中 
int fread(void *ptr,int size,int nitems,FILE *stream) 
從流stream中讀入nitems個長度爲size的字符串存入ptr中 
int fwrite(void *ptr,int size,int nitems,FILE *stream) 
向流stream中寫入nitems個長度爲size的字符串,字符串在ptr中 
int fscanf(FILE *stream,char *format[,argument,…]) 
以格式化形式從流stream中讀入一個字符串 
int vfscanf(FILE *stream,char *format,Valist param) 
以格式化形式從流stream中讀入一個字符串,參數從Valist param中取得 
int fprintf(FILE *stream,char *format[,argument,…]) 
以格式化形式將一個字符串寫給指定的流stream 
int vfprintf(FILE *stream,char *format,Valist param) 
以格式化形式將一個字符串寫給指定的流stream,參數從Valist param中取得 
int fseek(FILE *stream,long offset,int fromwhere) 
函數把文件指針移到fromwhere所指位置的向後offset個字節處,fromwhere能夠爲如下值: 
SEEK_SET 文件開關 SEEK_CUR 當前位置 SEEK_END 文件尾 
long ftell(FILE *stream) 函數返回定位在stream中的當前文件指針位置,以字節表示 
int rewind(FILE *stream) 將當前文件指針stream移到文件開頭 
int feof(FILE *stream) 檢測流stream上的文件指針是否在結束位置 
int fileno(FILE *stream) 取流stream上的文件處理,並返回文件處理 
int ferror(FILE *stream) 檢測流stream上是否有讀寫錯誤,若有錯誤就返回1 
void clearerr(FILE *stream) 清除流stream上的讀寫錯誤 
void setbuf(FILE *stream,char *buf) 給流stream指定一個緩衝區buf 
void setvbuf(FILE *stream,char *buf,int type,unsigned size) 
給流stream指定一個緩衝區buf,大小爲size,類型爲type,type的值見下表

┌───┬───────────────────────────────┐ 
│type值│意義 │ 
├───┼───────────────────────────────┤ 
│_IOFBF│文件是徹底緩衝區,當緩衝區是空時,下一個輸入操做將企圖填滿整個緩│ 
│ │衝區.在輸出時,在把任何數據寫到文件以前,將徹底填充緩衝區. │ 
│_IOLBF│文件是行緩衝區.當緩衝區爲空時,下一個輸入操做將仍然企圖填整個緩│ 
│ │衝區.然而在輸出時,每當新行符寫到文件,緩衝區就被清洗掉. │ 
│_IONBF│文件是無緩衝的.buf和size參數是被忽略的.每一個輸入操做將直接從文 │ 
│ │件讀,每一個輸出操做將當即把數據寫到文件中. │ 
└───┴───────────────────────────────┘ 
int fclose(FILE *stream) 關閉一個流,能夠是文件或設備(例如LPT1) 
int fcloseall() 關閉全部除stdin或stdout外的流 
int fflush(FILE *stream) 
關閉一個流,並對緩衝區做處理處理即對讀的流,將流內內容讀入緩衝區;對寫的流,將緩衝區內內容寫入流。成功返回0 
int fflushall() 
關閉全部流,並對流各自的緩衝區做處理處理即對讀的流,將流內內容讀入緩衝區;對寫的流,將緩衝區內內容寫入流。成功返回0 
int access(char *filename,int amode) 
本函數檢查文件filename並返回文件的屬性, 函數將屬性存於amode中,amode由如下位的組合構成 
06能夠讀、寫 04能夠讀 02能夠寫 01執行(忽略的) 00文件存在 
若是filename是一個目錄,函數將只肯定目錄是否存在函數執行成功返回0,不然返回-1 
int chmod(char *filename,int permiss) 本函數用於設定文件filename的屬性 
permiss能夠爲如下值 
S_IWRITE容許寫 S_IREAD容許讀 S_IREAD|S_IWRITE容許讀、寫 
int _chmod(char *filename,int func[,int attrib]); 
本函數用於讀取或設定文件filename的屬性, 
當func=0時,函數返回文件的屬性;當func=1時,函數設定文件的屬性 
若爲設定文件屬性,attrib能夠爲下列常數之一 
FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統

接口子程序,所在函數庫爲os.h、bios.h 
unsigned sleep(unsigned seconds) 暫停seconds微秒(百分之一秒) 
int unlink(char *filename) 刪除文件filename 
unsigned FP_OFF(void far *farptr) 本函數用來取遠指針farptr的偏移量 
unsigned FP_SEG(void far *farptr) 本函數用來沒置遠指針farptr的段值 
void far *MK_FP(unsigned seg,unsigned off)根據段seg和偏移量off構造一個far指針 
unsigned getpsp() 取程序段前綴的段地址,並返回這個地址 
char *parsfnm(char *cmdline,struct fcb *fcbptr,int option) 
函數分析一個字符串,一般,對一個文件名來講,是由cmdline所指的一個命令行. 
文件名是放入一個FCB中做爲一個驅動器,文件名和擴展名.FCB是由fcbptr所指定的. 
option參數是DOS分析系統調用時,AL文本的值. 
int absread(int drive,int nsects,int sectno,void *buffer) 
本函數功能爲讀特定的磁盤扇區, 
drive爲驅動器號(0=A,1=B等),nsects爲要讀的扇區數,sectno爲開始的邏輯扇區號,buffer爲保存所讀數據的保存空間 
int abswrite(int drive,int nsects,int sectno,void *buffer) 
本函數功能爲寫特定的磁盤扇區, 
drive爲驅動器號(0=A,1=B等),nsects爲要寫的扇區數,sectno爲開始的邏輯扇區號,buffer爲保存所寫數據的所在空間 
void getdfree(int drive,struct dfree *dfreep) 
本函數用來取磁盤的自由空間, 
drive爲磁盤號(0=當前,1=A等).函數將磁盤特性的由dfreep指向的dfree結構中. dfree結構以下: 
┌───────────────────┐ 
│struct dfree │ 
│{ │ 
│ unsigned df_avail; /*有用簇個數*/ │ 
│ unsigned df_total; /*總共簇個數*/ │ 
│ unsigned df_bsec; /*每一個扇區字節數*/│ 
│ unsigned df_sclus; /*每一個簇扇區數*/ │ 
│} │ 
└───────────────────┘ 
char far *getdta() 取磁盤轉換地址DTA 
void setdta(char far *dta) 設置磁盤轉換地址DTA 
void getfat(int drive,fatinfo *fatblkp) 
本函數返回指定驅動器drive(0=當前,1=A,2=B等)的文件分配表信息並存入結構fatblkp中,結構以下: 
┌──────────────────┐ 
│struct fatinfo │ 
│{ │ 
│ char fi_sclus; /*每一個簇扇區數*/ │ 
│ char fi_fatid; /*文件分配表字節數*/│ 
│ int fi_nclus; /*簇的數目*/ │ 
│ int fi_bysec; /*每一個扇區字節數*/ │ 
│} │ 
└──────────────────┘ 
void getfatd(struct fatinfo *fatblkp) 本函數返回當前驅動器的文件分配表信息, 並存入結構fatblkp中,結構以下: 
┌──────────────────┐ 
│struct fatinfo │ 
│{ │ 
│ char fi_sclus; /*每一個簇扇區數*/ │ 
│ char fi_fatid; /*文件分配表字節數*/│ 
│ int fi_nclus; /*簇的數目*/ │ 
│ int fi_bysec; /*每一個扇區字節數*/ │ 
│} │ 
└──────────────────┘ 
int bdos(int dosfun,unsigned dosdx,unsigned dosal) 
本函數對MSDOS系統進行調用, dosdx爲寄存器dx的值,dosal爲寄存器al的值,dosfun爲功能號 
int bdosptr(int dosfun,void *argument,unsiigned dosal) 
本函數對MSDOS系統進行調用, 
argument爲寄存器dx的值,dosal爲寄存器al的值,dosfun爲功能號 
int int86(int intr_num,union REGS *inregs,union REGS *outregs) 
執行intr_num號中斷,用戶定義的寄存器值存於結構inregs中, 執行完後將返回的寄存器值存於結構outregs中. 
int int86x(int intr_num,union REGS *inregs,union REGS *outregs, struct SREGS *segregs) 
執行intr_num號中斷,用戶定義的寄存器值存於結構inregs中和結構segregs中,執行完後將返回的寄存器值存於結構outregs中. 
int intdos(union REGS *inregs,union REGS *outregs) 
本函數執行DOS中斷0x21來調用一個指定的DOS函數,用戶定義的寄存器值存於結構inregs中,執行完後函數將返回的寄存器值存於結構outregs中 
int intdosx(union REGS *inregs,union REGS *outregs,struct SREGS *segregs) 
本函數執行DOS中斷0x21來調用一個指定的DOS函數,用戶定義的寄存器值存於結構inregs和segregs中,執行完後函數將返回的寄存器值存於結構outregs中 
void intr(int intr_num,struct REGPACK *preg) 
本函數中一個備用的8086軟件中斷接口它能產生一個由參數intr_num指定的8086軟件中斷. 
函數在執行軟件中斷前, 從結構preg複製用戶定義的各寄存器值到各個寄存器.軟件中斷完成後, 
函數將當前各個寄存器的值複製到結構preg中.參數以下: 
intr_num 被執行的中斷號,preg爲保存用戶定義的寄存器值的結構,結構以下 
┌──────────────────────┐ 
│struct REGPACK │ 
│{ │ 
│ unsigned r_ax,r_bx,r_cx,r_dx; │ 
│ unsigned r_bp,r_si,r_di,r_ds,r_es,r_flags; │ 
│} │ 
└──────────────────────┘ 
函數執行完後,將新的寄存器值存於結構preg中 
void keep(int status,int size) 
以status狀態返回MSDOS,但程序仍保留於內存中,所佔用空間由size決定. 
void ctrlbrk(int (*fptr)()) 設置中斷後的對中斷的處理程序. 
void disable() 禁止發生中斷 
void enable() 容許發生中斷 
void geninterrupt(int intr_num) 執行由intr_num所指定的軟件中斷 
void interrupt(* getvect(int intr_num))() 
返回中斷號爲intr_num的中斷處理程序, 例如: old_int_10h=getvect(0x10); 
void setvect(int intr_num,void interrupt(* isr)()) 
設置中斷號爲intr_num的中斷處理程序爲isr,例如: setvect(0x10,new_int_10h); 
void harderr(int (*fptr)()) 
定義一個硬件錯誤處理程序, 每當出現錯誤時就調用fptr所指的程序 
void hardresume(int rescode) 硬件錯誤處理函數 
void hardretn(int errcode) 硬件錯誤處理函數 
int inport(int prot) 從指定的輸入端口讀入一個字,並返回這個字 
int inportb(int port) 從指定的輸入端口讀入一個字節,並返回這個字節 
void outport(int port,int word) 將字word寫入指定的輸出端口port 
void outportb(int port,char byte) 將字節byte寫入指定的輸出端口port 
int peek(int segment,unsigned offset) 
函數返回segmentffset處的一個字 
char peekb(int segment,unsigned offset) 
函數返回segmentffset處的一個字節 
void poke(int segment,int offset,char value) 
將字value寫到segmentffset處 
void pokeb(int segment,int offset,int value) 
將字節value寫到segmentffset處 
int randbrd(struct fcb *fcbptr,int reccnt) 
函數利用打開fcbptr所指的FCB讀reccnt個記錄. 
int randbwr(struct fcb *fcbptr,int reccnt) 
函數將fcbptr所指的FCB中的reccnt個記錄寫到磁盤上 
void segread(struct SREGS *segtbl)函數把段寄存器的當前值放進結構segtbl中 
int getverify() 取檢驗標誌的當前狀態(0=檢驗關閉,1=檢驗打開) 
void setverify(int value) 
設置當前檢驗狀態, value爲0表示關閉檢驗,爲1表示打開檢驗 
int getcbrk()本函數返回控制中斷檢測的當前設置 
int setcbrk(int value)本函數用來設置控制中斷檢測爲接通或斷開當value=0時,爲斷開檢測.當value=1時,爲接開檢測 
int dosexterr(struct DOSERR *eblkp) 
取擴展錯誤.在DOS出現錯誤後,此函數將擴充的錯誤信息填入eblkp所指的DOSERR結構中.該結構定義以下: 
┌──────────────┐ 
│struct DOSERR │ 
│{ │ 
│ int exterror;/*擴展錯誤*/ │ 
│ char class; /*錯誤類型*/ │ 
│ char action; /*方式*/ │ 
│ char locus; /*錯誤場所*/ │ 
│} │ 
└──────────────┘ 
int bioscom(int cmd,char type,int port) 本函數負責對數據的通信工做, 
cmd能夠爲如下值: 
0 置通信參數爲字節byte值 1 發送字符經過通信線輸出 
2 從通信線接受字符 3 返回通信的當前狀態 
port爲通信端口,port=0時通信端口爲COM1,port=1時通信端口爲COM2,以此類推 
byte爲傳送或接收數據時的參數,爲如下位的組合: 
┌───┬─────┬───┬─────┬───┬─────┐ 
│byte值│意義 │byte值│意義 │byte值│意義 │ 
├───┼─────┼───┼─────┼───┼─────┤ 
│0x02 │7數據位 │0x03 │8數據位 │0x00 │1中止位 │ 
│0x04 │2中止位 │0x00 │無奇偶性 │0x08 │奇數奇偶性│ 
│0x18 │偶數奇偶性│0x00 │110波特 │0x20 │150波特 │ 
│0x40 │300波特 │0x60 │600波特 │0x80 │1200波特 │ 
│0xA0 │2400波特 │0xC0 │4800波特 │0xE0 │9600波特 │ 
└───┴─────┴───┴─────┴───┴─────┘

例如:0xE0|0x08|0x00|0x03即表示置通信口爲9600波特,奇數奇偶性,1中止位, 
8數據位. 函數返回值爲一個16位整數,定義以下: 
第15位 超時 
第14位 傳送移位寄存器空 
第13位 傳送固定寄存器空 
第12位 中斷檢測 
第11位 幀錯誤 
第10位 奇偶錯誤 
第 9位 過載運行錯誤 
第 8位 數據就緒 
第 7位 接收線信號檢測 
第 6位 環形指示器 
第 5位 數據設置就緒 
第 4位 清除發送 
第 3位 δ接收線信號檢測器 
第 2位 降低邊環形檢測器 
第 1位 δ數據設置就緒 
第 0位 δ清除發送

int biosdisk(int cmd,int drive,int head,int track, int sector,int nsects,void *buffer) 
本函數用來對驅動器做必定的操做,cmd爲功能號, drive爲驅動器號(0=A,1=B,0x80=C,0x81=D,0x82=E等).cmd可爲如下值:

0 重置軟磁盤系統.這強迫驅動器控制器來執行硬復位.忽略全部其它參數. 
1 返回最後的硬盤操做狀態.忽略全部其它參數 
2 讀一個或多個磁盤扇區到內存.讀開始的扇區由head、track、sector給出。扇區號由nsects給出。把每一個扇區512個字節的數據讀入buffer 
3 從內存讀數據寫到一個或多個扇區。寫開始的扇區由head、track、sector給出。扇區號由nsects給出。所寫數據在buffer中,每扇區512個字節。 
4 檢驗一個或多個扇區。開始扇區由head、track、sector給出。扇區號由nsects給出。 
5 格式化一個磁道,該磁道由head和track給出。buffer指向寫在指定track上的扇區磁頭器的一個表。如下cmd值只容許用於XT或AT微機: 
6 格式化一個磁道,並置壞扇區標誌。 
7 格式化指定磁道上的驅動器開頭。 
8 返回當前驅動器參數,驅動器信息返回寫在buffer中(以四個字節表示)。 
9 初始化一對驅動器特性。 
10 執行一個長的讀,每一個扇區讀512加4個額外字節 
11 執行一個長的寫,每一個扇區寫512加4個額外字節 
12 執行一個磁盤查找 
13 交替磁盤復位 
14 讀扇區緩衝區 
15 寫扇區緩衝區 
16 檢查指定的驅動器是否就緒 
17 複覈驅動器 
18 控制器RAM診斷 
19 驅動器診斷 
20 控制器內部診

函數返回由下列位組合成的狀態字節: 
0x00 操做成功 
0x01 壞的命令 
0x02 地址標記找不到 
0x04 記錄找不到 
0x05 重置失敗 
0x07 驅動參數活動失敗 
0x09 企圖DMA通過64K界限 
0x0B 檢查壞的磁盤標記 
0x10 壞的ECC在磁盤上讀 
0x11 ECC校訂的數據錯誤(注意它不是錯誤) 
0x20 控制器失效 
0x40 查找失敗 
0x80 響應的鏈接失敗 
0xBB 出現無定義錯誤 
0xFF 讀出操做失敗

int biodquip() 
檢查設備,函數返回一字節,該字節每一位表示一個信息,以下: 
第15位 打印機號 
第14位 打印機號 
第13位 未使用 
第12位 鏈接遊戲I/O 
第11位 RS232端口號 
第 8位 未使用 
第 7位 軟磁盤號 
第 6位 軟磁盤號, 
00爲1號驅動器,01爲2號驅動器,10爲3號驅動器,11爲4號驅動器

第 5位 初始化 
第 4位 顯示器模式 
00爲未使用,01爲40x25BW彩色顯示卡 
10爲80x25BW彩色顯示卡,11爲80x25BW單色顯示卡 
第 3位 母扦件 
第 2位 隨機存貯器容量,00爲16K,01爲32K,10爲48K,11爲64K 
第 1位 浮點共用處理器 
第 0位 從軟磁盤引導

int bioskey(int cmd)本函數用來執行各類鍵盤操做,由cmd肯定操做。 
cmd可爲如下值: 
0 返回敲鍵盤上的下一個鍵。若低8位爲非0,即爲ASCII字符;若低8位爲0, 
則返回擴充了的鍵盤代碼。 
1 測試鍵盤是否可用於讀。返回0表示沒有鍵可用;不然返回下一次敲鍵之值。 
敲鍵自己一直保持由下次調用具的cmd值爲0的bioskey所返回的值。 
2 返回當前的鍵盤狀態,由返回整數的每個位表示,見下表: 
┌──┬───────────┬───────────┐ 
│ 位 │爲0時意義 │爲1時意義 │ 
├──┼───────────┼───────────┤ 
│ 7 │插入狀態 │改寫狀態 │ 
│ 6 │大寫狀態 │小寫狀態 │ 
│ 5 │數字狀態,NumLock燈亮 │光標狀態,NumLock燈熄 │ 
│ 4 │ScrollLock燈亮 │ScrollLock燈熄 │ 
│ 3 │Alt按下 │Alt未按下 │ 
│ 2 │Ctrl按下 │Ctrl未按下 │ 
│ 1 │左Shift按下 │左Shift未按下 │ 
│ 0 │右Shift按下 │右Shift未按下 │ 
└──┴───────────┴───────────┘ 
int biosmemory() 返回內存大小,以K爲單位. 
int biosprint(int cmd,int byte,int port) 控制打印機的輸入/輸出. 
port爲打印機號,0爲LPT1,1爲LPT2,2爲LPT3等 
cmd能夠爲如下值: 
0 打印字符,將字符byte送到打印機 
1 打印機端口初始化 
2 讀打印機狀態 
函數返回值由如下位值組成表示當前打印機狀態 
0x01 設備時間超時 
0x08 輸入/輸出錯誤 
0x10 選擇的 
0x20 走紙 
0x40 承認 
0x80 不忙碌

int biostime(int cmd,long newtime)計時器控制,cmd爲功能號,可爲如下值

0 函數返回計時器的當前值 
1 將計時器設爲新值newtime

struct country *country(int countrycmode,struct country *countryp) 
本函數用來控制某一國家的相關信息,如日期,時間,貨幣等. 
若countryp=-1時,當前的國家置爲countrycode值(必須爲非0).不然,由countryp所指向的country結構用下列的國家相關信息填充: 
(1)當前的國家(若countrycode爲0或2)由countrycode所給定的國家. 
結構country定義以下: 
┌────────────────────┐ 
│struct country │ 
│{ │ 
│ int co_date; /*日期格式*/ │ 
│ char co_curr[5]; /*貨幣符號*/ │ 
│ char co_thsep[2]; /*數字分隔符*/ │ 
│ char co_desep[2]; /*小數點*/ │ 
│ char co_dtsep[2]; /*日期分隔符*/ │ 
│ char co_tmsep[2]; /*時間分隔符*/ │ 
│ char co_currstyle; /*貨幣形式*/ │ 
│ char co_digits; /*有效數字*/ │ 
│ int (far *co_case)(); /*事件處理函數*/ │ 
│ char co_dasep; /*數據分隔符*/ │ 
│ char co_fill[10]; /*補充字符*/ │ 
│} │ 
└────────────────────┘ 
co_date的值所表明的日期格式是: 
0 月日年 1 日月年 2 年月日 
co_currstrle的值所表明的貨幣顯示方式是 
0 貨幣符號在數值前,中間無空格 
1 貨幣符號在數值後,中間無空格 
2 貨幣符號在數值前,中間有空格 
3 貨幣符號在數值後,中間有空格

操做函數,所在函數庫爲string.h、mem.h 
mem…操做存貯數組 
void *memccpy(void *destin,void *source,unsigned char ch,unsigned n) 
void *memchr(void *s,char ch,unsigned n) 
void *memcmp(void *s1,void *s2,unsigned n) 
int memicmp(void *s1,void *s2,unsigned n) 
void *memmove(void *destin,void *source,unsigned n) 
void *memcpy(void *destin,void *source,unsigned n) 
void *memset(void *s,char ch,unsigned n)

這些函數,mem…系列的全部成員均操做存貯數組.在全部這些函數中,數組是n字節長. 
memcpy從source複製一個n字節的塊到destin.若是源塊和目標塊重迭,則選擇複製方向, 以例正確地複製覆蓋的字節. 
memmove與memcpy相同. memset將s的全部字節置於字節ch中.s數組的長度由n給出. 
memcmp比較正好是n字節長的兩個字符串s1和s2.些函數按無符號字符比較字節,所以, 
memcmp("0xFF","/x7F",1)返回值大於0. memicmp比較s1和s2的前n個字節,無論字符大寫或小寫. 
memccpy從source複製字節到destin.複製一結束就發生下列任一狀況: 
(1)字符ch首選複製到destin. 
(2)n個字節已複製到destin. 
memchr對字符ch檢索s數組的前n個字節. 
返回值:memmove和memcpy返回destin 
memset返回s的值 
memcmp和memicmp─┬─若s1<s2返回值小於0 
├─若s1=s2返回值等於0 
└─若s1>s2返回值大於0 
memccpy若複製了ch,則返回直接跟隨ch的在destin中的字節的一個指針;

不然返回NULL 
memchr返回在s中首先出現ch的一個指針;若是在s數組中不出現ch,就返回NULL.

void movedata(int segsrc,int offsrc, int segdest,int offdest, unsigned numbytes) 
本函數將源地址(segsrcffsrc)處的numbytes個字節複製到目標地址(segdestffdest) 
void movemem(void *source,void *destin,unsigned len) 
本函數從source處複製一塊長len字節的數據到destin.若源地址和目標地址字符串重迭,則選擇複製方向,以便正確的複製數據. 
void setmem(void *addr,int len,char value) 
本函數把addr所指的塊的第一個字節置於字節value中.

str…字符串操做函數 
char stpcpy(char *dest,const char *src) 將字符串src複製到dest 
char strcat(char *dest,const char *src) 將字符串src添加到dest末尾 
char strchr(const char *s,int c) 檢索並返回字符c在字符串s中第一次出現的位置 
int strcmp(const char *s1,const char *s2) 比較字符串s1與s2的大小,並返回s1-s2 
char strcpy(char *dest,const char *src) 將字符串src複製到dest 
size_t strcspn(const char *s1,const char *s2) 掃描s1,返回在s1中有,在s2中也有的字符個數 
char strdup(const char *s) 將字符串s複製到最近創建的單元 
int stricmp(const char *s1,const char *s2) 比較字符串s1和s2,並返回s1-s2 
size_t strlen(const char *s) 返回字符串s的長度 
char strlwr(char *s) 
將字符串s中的大寫字母所有轉換成小寫字母,並返回轉換後的字符串 
char strncat(char *dest,const char *src,size_t maxlen) 
將字符串src中最多maxlen個字符複製到字符串dest中 
int strncmp(const char *s1,const char *s2,size_t maxlen) 
比較字符串s1與s2中的前maxlen個字符 
char strncpy(char *dest,const char *src,size_t maxlen) 
複製src中的前maxlen個字符到dest中 
int strnicmp(const char *s1,const char *s2,size_t maxlen) 
比較字符串s1與s2中的前maxlen個字符 
char strnset(char *s,int ch,size_t n) 
將字符串s的前n個字符置於ch中 
char strpbrk(const char *s1,const char *s2) 
掃描字符串s1,並返回在s1和s2中均有的字符個數 
char strrchr(const char *s,int c) 
掃描最後出現一個給定字符c的一個字符串s 
char strrev(char *s) 
將字符串s中的字符所有顛倒順序從新排列,並返回排列後的字符串 
char strset(char *s,int ch) 
將一個字符串s中的全部字符置於一個給定的字符ch 
size_t strspn(const char *s1,const char *s2) 
掃描字符串s1,並返回在s1和s2中均有的字符個數 
char strstr(const char *s1,const char *s2) 
掃描字符串s2,並返回第一次出現s1的位置 
char strtok(char *s1,const char *s2) 
檢索字符串s1,該字符串s1是由字符串s2中定義的定界符所分隔 
char strupr(char *s) 
將字符串s中的小寫字母所有轉換成大寫字母,並返回轉換後的字符串

存貯分配子程序,所在函數庫爲dos.h、alloc.h、malloc.h、stdlib.h、process.h 
int allocmem(unsigned size,unsigned *seg) 
利用DOS分配空閒的內存, size爲分配內存大小,seg爲分配後的內存指針 
int freemem(unsigned seg) 
釋放先前由allocmem分配的內存,seg爲指定的內存指針 
int setblock(int seg,int newsize) 
本函數用來修改所分配的內存長度, seg爲已分配內存的內存指針,newsize爲新的長度 
int brk(void *endds) 
本函數用來改變分配給調用程序的數據段的空間數量,新的空間結束地址爲endds 
char *sbrk(int incr) 
本函數用來增長分配給調用程序的數據段的空間數量,增長incr個字節的空間 
unsigned long coreleft() 本函數返回未用的存儲區的長度,以字節爲單位 
void *calloc(unsigned nelem,unsigned elsize) 
分配nelem個長度爲elsize的內存空間並返回所分配內存的指針 
void *malloc(unsigned size) 分配size個字節的內存空間,並返回所分配內存的指針 
void free(void *ptr) 釋放先前所分配的內存,所要釋放的內存的指針爲ptr 
void *realloc(void *ptr,unsigned newsize) 
改變已分配內存的大小,ptr爲已分配有內存區域的指針,newsize爲新的長度,返回分配好的內存指針. 
long farcoreleft() 本函數返回遠堆中未用的存儲區的長度,以字節爲單位 
void far *farcalloc(unsigned long units,unsigned long unitsz) 
從遠堆分配units個長度爲unitsz的內存空間,並返回所分配內存的指針 
void *farmalloc(unsigned long size) 
分配size個字節的內存空間, 並返回分配的內存指針 
void farfree(void far *block) 
釋放先前從遠堆分配的內存空間, 所要釋放的遠堆內存的指針爲block 
void far *farrealloc(void far *block,unsigned long newsize) 
改變已分配的遠堆內存的大小,block爲已分配有內存區域的指針,newzie爲新的長度,返回分配好的內存指針

時間日期函數,函數庫爲time.h、dos.h 在時間日期函數裏,主要用到的結構有如下幾個: 總時間日期貯存結構tm ┌──────────────────────┐ │struct tm │ │{ │ │ int tm_sec; /*秒,0-59*/ │ │ int tm_min; /*分,0-59*/ │ │ int tm_hour; /*時,0-23*/ │ │ int tm_mday; /*天數,1-31*/ │ │ int tm_mon; /*月數,0-11*/ │ │ int tm_year; /*自1900的年數*/ │ │ int tm_wday; /*自星期日的天數0-6*/ │ │ int tm_yday; /*自1月1日起的天數,0-365*/ │ │ int tm_isdst; /*是否採用夏時制,採用爲正數*/│ │} │ └──────────────────────┘ 日期貯存結構date ┌───────────────┐ │struct date │ │{ │ │ int da_year; /*自1900的年數*/│ │ char da_day; /*天數*/ │ │ char da_mon; /*月數 1=Jan*/ │ │} │ └───────────────┘ 時間貯存結構time ┌────────────────┐ │struct time │ │{ │ │ unsigned char ti_min; /*分鐘*/│ │ unsigned char ti_hour; /*小時*/│ │ unsigned char ti_hund; │ │ unsigned char ti_sec; /*秒*/ │ │ │ └────────────────┘ char *ctime(long *clock) 本函數把clock所指的時間(如由函數time返回的時間)轉換成下列格式的字符串: Mon Nov 21 11:31:54 1983/n/0 char asctime(struct tm *tm) 本函數把指定的tm結構類的時間轉換成下列格式的字符串: Mon Nov 21 11:31:54 1983/n/0 double difftime(time_t time2,time_t time1) 計算結構time2和time1之間的時間差距(以秒爲單位) struct tm *gmtime(long *clock) 本函數把clock所指的時間(如由函數time返回的時間)轉換成格林威治時間,並以tm結構形式返回 struct tm *localtime(long *clock) 本函數把clock所指的時間(如函數time返回的時間)轉換成當地標準時間,並以tm結構形式返回 void tzset()本函數提供了對UNIX操做系統的兼容性 long dostounix(struct date *dateptr,struct time *timeptr) 本函數將dateptr所指的日期,timeptr所指的時間轉換成UNIX格式, 並返回自格林威治時間1970年1月1日凌晨起到如今的秒數 void unixtodos(long utime,struct date *dateptr,struct time *timeptr) 本函數將自格林威治時間1970年1月1日凌晨起到如今的秒數utime轉換成DOS格式並保存於用戶所指的結構dateptr和timeptr中 void getdate(struct date *dateblk) 本函數將計算機內的日期寫入結構dateblk中以供用戶使用 void setdate(struct date *dateblk) 本函數將計算機內的日期改爲由結構dateblk所指定的日期 void gettime(struct time *timep) 本函數將計算機內的時間寫入結構timep中, 以供用戶使用 void settime(struct time *timep) 本函數將計算機內的時間改成由結構timep所指的時間 long time(long *tloc) 本函數給出自格林威治時間1970年1月1日凌晨至如今所通過的秒數,並將該值存於tloc所指的單元中. int stime(long *tp)本函數將tp所指的時間(例如由time所返回的時間)寫入計算機中

相關文章
相關標籤/搜索