Ⅰ C語言要用的函數有哪些
我是計算機專業的,當初學c語言的時候也是好為難,也有一些心得體會
語言這種東西會隨著接觸的多就自然全都通了,需要時間,慢慢來,看到你問的這些問題跟我當初好像,感覺你學的支離破散,我建議你胡謹要系統的學c語言,
我有一個c語言的教程,txt的,我可以發給你,看看很有幫助,你問這些問題,即使通了也是不透徹
函數中包含了程序的可執行代碼。每個C程序的入口和出口都位於函數main()之中。main()函數可以調用其他函數,這些函數執行完畢後程序的控制又返回到main()函數中,main()函數不能被別的函數所調用。通常我們把這些被調用的函數稱為下層(lower-level)函數。函數調用發生時,立即執行被調用的函數,而調用者則進入等待狀態,直到被調用函數執行完畢。函數可以有參數和返回值。
程序員一般把函數當作「黑箱」處理,並不關心它內部的實現細節。當然程序員也可以自己開發函數庫。
說明一點,函數這一節很重要,可以說一個程序的優劣集中體現在函數上。如果函數使用的恰當,可以讓程序看起來有條理,容易看懂。如果函數使用的亂七八糟,或者是沒有使用函數,程序就會顯得很亂,不僅讓別人無法查看,就連自己也容易暈頭轉向。可以這樣說,如果超過100行的程序中沒有使用函數,那麼這個程序一定很羅嗦(有些絕對,但也是事實)。
一、函數的定義
一個函數包括函數頭和語句漏蘆體兩部分。
函數頭由下列三不分組成:
函數返回值類型
函數名
參數表
一個完整的函數應該是這樣的:
函數返回值類型 函數名(參數表)
{
語句體;
}
函數返回值類型可以是前面說到的某個數據類型、或者是某個數據類型的指針、指向結構的指針、指向數組的指針。指針概念到以後再介紹。
函數名在程序中必須是唯一的,它也遵循標識符命名規則。
參數表可以沒有也可以有多個,在函數調用的時候,實際參數將被拷貝到這些變數中。語句體包括局部變數的聲明和可執行代碼。
我們在前面其實已經接觸過函數了,如abs(),sqrt(),我們並不知道它的內部是什返做帶么,我們只要會使用它即可。
這一節主要講解無參數無返回值的函數調用。
Ⅱ 在C語言中有那些函數名
想必你說的是C語言內置的標准函數庫了
給你貼一下吧
想查閱更詳細信息可以到http://woyaoxuexi.net/pro/c/hsk/Index.html
下載到http://www.playicq.cn/3/5988.html
分類函數,所在函數庫為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 p(int handle) 復制一個文件處理指針handle,返回這個指針
int p2(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、p、p2、_open、open中的一個處調用獲得的文件處理
成功返回0否則返回-1,可用於UNIX系統
int _close(int handle) 關閉handle所表示的文件處理,handle是從_creat、creat、
creatnew、creattemp、p、p2、_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寫入
Ⅲ 編程5分鍾,命名2小時!大神程序員都在用這套命名方法
在 軟體中隨處可見命名:要給變數、函數、參數、類和封包命名,還要給源代碼及源代碼所在目錄命名,甚至還有jar文件、war文件和ear文件命名。
但是,看似簡單的命名,也是讓不少程序員頭疼的問題。 有一些小夥伴,在進行變數命名的時候,對於自己熟悉的英文,可能還會用英文命名一下,如果需要命名的部分不會用英文表達,或許就直接用拼音了。
有的童鞋一下想不起來怎麼命名,直接用拼音直接用aa,bb等這樣沒有任何代表意義的字母來命名,可讀性非常差,可能自己今天寫的,一個星期後回來再看,也忘記其具體代表的含義了。
因此,許多人在寫代碼之前,總會在想啊想啊,用什麼命名法好呢?對於經常在C++、Java、Python等主流語言上切換的強迫症來說,換個語言換種命名風格簡直不要太混亂。
既然有這么多命名要做,不妨做好它。本期內容中,非同步君為大家帶來了起個好名字應遵從的幾條簡單規則,一起來看看吧
— 01 —
名副其實
名副其實說起來簡單。我們想要強調,這事很嚴肅。選個好名字要花時間,但省下來的時間比花掉的多。注意命名,而且一旦發現有更好的名稱,就換掉舊的。這么做,讀你代碼的人(包括你自己)都會更開心。
變數、函數或類的名稱應該已經答復了所有的大問題。它該告訴你,它為什麼會存在,它做什麼事,應該怎麼用。如果名稱需要注釋來補充,那就不算是名副其實。
名稱d什麼也沒說明。它沒有引起讀者對時間消逝的感覺,更別說以日計了。我們應該選擇指明了計量對象和計量單位的名稱:
選擇體現本意的名稱能讓人更容易理解和修改代碼。下列代碼的目的何在?
為什麼難以說明上述代碼要做什麼事?裡面並沒有復雜的表達式,空格和縮進中規中矩,只用到三個變數和兩個常量,甚至沒有涉及任何其他類或多態方法,只是(或者看起來是)一個數組的列表而已。
問題不在於代碼的簡潔度,而在於代碼的模糊度:即上下文在代碼中未被明確體現的程度。上述代碼要求我們了解類似以下問題的答案:
(1)theList中是什麼類型的東西?
(2)theList零下標條目的意義是什麼?
(3)值4的意義是什麼?
(4)我怎麼使用返回的列表?
問題的答案沒體現在代碼段中,可代碼段就是它們該在的地方。比方說,我們在開發一種掃雷 游戲 ,我們發現,盤面是名為theList的單元格列表,那就將其名稱改為gameBoard。
盤面上每個單元格都用一個簡單數組表示。我們還發現,零下標條目是一種狀態值,而該種狀態值為4表示「已標記」。只要改為有意義的名稱,代碼就會得敏液到相當程度的改進:
注意,代碼的簡潔性並未被觸及。運算符和常量的數量全然保持不變,嵌套數量也全然保持不變,但代碼變得明確多了。
還可以更進一步,不用int數組表示單元格,而是另寫一個類。該類包括一個名副其實的函數(稱為isFlagged),從而掩蓋住那個魔術數[1]。於是得到函數的新版本:
只要簡單改一下名稱,就能輕易知道發生了什麼。這就是選液含用好名稱的力量。
— 02 —
避免誤導
程序員必須避免留下掩藏代碼本意的錯誤線索。應當避免使用與本意相悖的詞,例如,hp、aix和sco都不該用作變數名,因為它們都是Unix平台或類Unix平台的專有名稱。即便你是在編寫三角計算程鬧拿笑序,hp看起來是一個不錯的縮寫[2],但那也可能會提供錯誤信息。
別用accountList來指稱一組賬號,除非它真的是List類型。List一詞對程序員有特殊意義。如果包納賬號的容器並非真是一個List,就會引起錯誤的判斷。
所以,用accountGroup或bunchOfAccounts,甚至直接用accounts都會好一些。
提防使用外形相似度較高的名稱。例如,想區分模塊中某處的XYZControllerFor-EfficientHandlingOfStrings和另一處的-OfStrings,會花多長時間呢?這兩個詞的外形實在太相似了。
以同樣的方式拼寫出同樣的概念才是信息。拼寫前後不一致就是誤導。我們很享受現代Java編程環境的自動代碼完成特性。鍵入某個名稱的前幾個字母,按一下某個熱鍵組合(如果有的話),就能得到一列該名稱的可能形式。
假如相似的名稱依字母順序放在一起,且差異很明顯,那就會相當有助益,因為程序員多半會壓根不看你的詳細注釋,甚至不看該類的方法列表就直接看名字挑一個對象。
誤導性名稱真正可怕的例子,是用小寫字母l和大寫字母O作為變數名,尤其是在組合使用的時候。當然,問題在於它們看起來完全像是常量「壹」和「零」。
讀者可能會認為這純屬虛構,但我們確曾見過充斥這類名稱的代碼。有一次,代碼作者建議用不同字體寫變數名,好顯得更清楚些,但前提是這種方案得要通過口頭和書面傳遞給未來所有的開發者才行。後來,只是做了簡單的重命名操作,就解決了問題,而且也沒引起別的問題。
— 03 —
做有意義的區分
如果程序員只是為滿足編譯器或解釋器的需要而寫代碼,就會製造麻煩。例如,因為同一作用范圍內兩樣不同的東西不能重名,你可能會隨手改掉其中一個的名稱,有時乾脆以錯誤的拼寫充數,結果就會出現在更正拼寫錯誤後導致編譯器出錯的情況。
光是添加數字系列或是廢話遠遠不夠,即便這足以讓編譯器滿意。如果名稱必須相異,那麼其意思也應該不同才對。
以數字系列命名(a1、a2…aN)是依義命名的對立面。這樣的名稱純屬誤導——完全沒有提供正確信息,沒有提供導向作者意圖的線索。試看:
如果參數名改為source和destination,這個函數就會像樣許多。
廢話是另一種沒意義的區分。假設你有一個Proct類,如果還有一個名為ProctInfo或ProctData的類,那它們的名稱雖然不同,意思卻無區別。Info和Data就像a、an和the一樣,是意義含混的廢話。
注意,只要體現出有意義的區分,使用a和the這樣的前綴就沒錯。例如,你可能把a用在域內變數,而把the用於函數參數[5]。但如果你已經有一個名為zork的變數,又想調用一個名為theZork的變數,麻煩就來了。
廢話都是冗餘。variable一詞永遠不應當出現在變數名中。table一詞永遠不應當出現在表名中。NameString會比Name好嗎?難道Name會是一個浮點數?如果是這樣,就違反了關於誤導的規則。
設想有一個名為Customer的類,還有一個名為CustomerObject的類,它們的區別何在呢?哪一個是表示客戶 歷史 支付情況的最佳方式?
有一個應用反映了這種狀況。為當事者諱,我們改了一下,不過犯錯的代碼的確就是這個樣子:
程序員怎麼知道該調用哪個函數呢?
如果缺少明確約定,那麼變數moneyAmount與money就沒區別,customerInfo與customer沒區別,accountData與account沒區別,theMessage也與message沒區別。要區分名稱,就要以讀者能鑒別不同之處的方式來區分。
— 04 —
使用讀得出來的名稱
人類長於記憶和使用單詞。大腦的相當一部分就是用來容納和處理單詞的。單詞能讀得出來。人類的大腦中有那麼大的一塊地方用來處理言語,若不善加利用,實在是種恥辱。
如果名稱讀不出來,討論的時候就會像個傻鳥。「哎,這兒,鼻涕阿三喜摁踢(bee cee arr three cee enn tee)[6]上頭,有個皮挨死極翹(pee ess zee kyew)[7]整數,看見沒?」這不是小事,因為編程本就是一種 社會 活動。
有一家公司,程序裡面寫了一個genymdhms(生成日期,年、月、日、時、分、秒),他們一般讀作「gen why emm dee aich emm ess」[8]。我有見字照拼讀的惡習,於是開口就念「gen-yah-mudda-hims」。
後來好些設計師和分析師都有樣學樣,聽起來傻乎乎的。我們知道典故,所以會覺得很 搞笑 。 搞笑 歸 搞笑 ,實際是在強忍糟糕的命名。在給新開發者解釋變數名的意義時,他們總是讀出傻乎乎的自造詞,而非恰當的英語詞。比較
現在讀起來就像人話了:「喂,Mikey,看看這條記錄!生成時間戳(generation timestamp)[9]被設置為明天了!不能這樣吧?」
— 05 —
使用可搜索的名稱
對於單字母名稱和數字常量,有一個問題,就是很難在一大篇文字中找出來。
找MAX_CLASSES_PER_STUDENT很容易,但想找數字7就麻煩了,它可能是某些文件名或其他常量定義的一部分,出現在因不同意圖而採用的各種表達式中。如果該常量是個長數字,又被人錯改過,就會逃過搜索,從而造成錯誤。
同樣,e也不是一個便於搜索的好變數名,它是英文中最常用的字母,在每個程序、每段代碼中都有可能出現。由此而見,長名稱勝於短名稱,搜得到的名稱勝於用自造編碼代寫就的名稱。
竊以為單字母名稱僅用於短方法中的本地變數。名稱長短應與其作用域大小相對應 [N5]。若變數或常量可能在代碼中多處使用,則應賦予其便於搜索的名稱。再比較:
注意,上面代碼中的sum並非特別有用的名稱,不過至少搜得到它。採用能表達意圖的名稱,貌似拉長了函數代碼,但要想想看,WORK_DAYS_PER_WEEK比數字5好找得多,而列表中也只剩下了體現作者意圖的名稱。
— 06 —
避免使用編碼
編碼已經太多,無謂再自找麻煩。把類型或作用域編進名稱裡面,徒然增加了解碼的負擔。沒理由要求每位新人都在弄清要應付的代碼之外(那算是正常的),還要再搞懂另一種編碼「語言」。這對解決問題而言,純屬多餘的負擔。帶編碼的名稱通常也不便發音,容易打錯。
匈牙利語標記法
在往昔名稱長短很重要的時代,我們毫無必要地破壞了不編碼的規矩,如今後悔不迭。Fortran語言要求首字母體現出類型,導致了編碼的產生。BASIC語言的早期版本只允許使用一個字母再加上一位數字。匈牙利語標記法[10](Hungarian Notation,HN)將這種態勢愈演愈烈。
在Windows的C語言API的時代,HN相當重要,那時所有名稱要麼是一個整數句柄,要麼是一個長指針或者void指針,要不然就是string的幾種實現(有不同的用途和屬性)之一。那時候編譯器並不做類型檢查,程序員需要匈牙利語標記法來幫助自己記住類型。
現代編程語言具有更豐富的類型系統,編譯器也記得並強制使用類型。而且,程序員趨向於使用更小的類、更短的方法,好讓每個變數的定義都在視野范圍之內。
Java程序員不需要類型編碼,因為對象是強類型的,代碼編輯環境已經先進到在編譯開始前就能監測到類型錯誤的程度!所以,如今HN和其他的類型編碼形式都純屬多餘。它們增加了修改變數、函數或類的名稱或類型的難度,它們增加了閱讀代碼的難度,它們製造了讓編碼系統誤導讀者的可能性。
成員前綴
也不必用m_前綴來標明成員變數。應當把類和函數做得足夠小,以消除對成員前綴的需要。你應當使用某種可以高亮或用顏色標出成員的編輯環境。
此外,人們會很快學會無視前綴(或後綴),而只看到名稱中有意義的部分。代碼讀得越多,眼中就越沒有前綴。最終,前綴變作了不入法眼的廢料,變作了舊代碼的標志物。
介面和實現
有時也會出現採用編碼的特殊情形。比如,你在做一個創建形狀用的抽象工廠(Abstract Factory),該工廠是一個介面,要用具體類來實現。你怎麼來命名工廠和具體類呢?IShapeFactory和ShapeFactory嗎?我喜歡不加修飾的介面。前導字母I被濫用到了說好聽點兒是干擾,說難聽點兒根本就是廢話的程度。
我不想讓用戶知道我給他們的是介面,而就想讓他們知道那是一個ShapeFactory。如果在介面和實現中必須選其一來編碼的話,我寧肯選擇實現。ShapeFactoryImp,甚至是醜陋的CShapeFactory,都比對介面名稱編碼好。
-END-
代碼整潔之道
作者: [美] 羅伯特·C. 馬丁(Robert C. Martin)
譯者: 韓磊
內容簡介:
軟體質量,不但依賴架構及項目管理,而且與代碼質量緊密相關。這一點,無論是敏捷開發流派還是傳統開發流派,都不得不承認。
本書提出一種觀點:代碼質量與其整潔度成正比。干凈的代碼,既在質量上較為可靠,也為後期維護、升級奠定了良好基礎。作為編程領域的佼佼者,本書作者給出了一系列行之有效的整潔代碼操作實踐。這些實踐在本書中體現為一條條規則(或稱「啟示」),並輔以來自實際項目的正、反兩面的範例。只要遵循這些規則,就能編寫出干凈的代碼,從而有效提升代碼質量。
本書閱讀對象為一切有志於改善代碼質量的程序員及技術經理。書中介紹的規則均來自作者多年的實踐經驗,涵蓋從命名到重構的多個編程方面,雖為一「家」之言,然誠有可資借鑒的價值。
Ⅳ 如何優雅地為程序中的變數和函數命名
簡言之,根據語意來選擇詞彙,別無它法……然而,有時我們會不知用什麼詞彙更合適。
當你想到某個抽象的東西,你更傾向於最先想到的詞語,除非你故意不這樣,這些詞也會搶著出現,直到模糊或改變你的想法。
當你想到一個具體的對象,你覺得詞窮,然後你想描述的已經看到了,然後你繼續尋找更適合它的詞。
哈哈,命名竟成了編程中最難的事~
Martin Fowler曾經在一篇文章中曾經引用過Phil Karlton的話:
There are only two hard things in Computer Science: cache invalidation
and naming things.
他說這句話在很長的一段時間內都是他最喜歡的話。可見命名對於廣大的程序員來說的確是個大問題。
對於我們中國人來說,問題可能出在兩個方面:
– 自打學編程開始就沒被教育過要重視命名。
這可以在譚浩強的《C語言入門》一書中可見一斑。《C語言入門》可以說是很多程序員在大學時學習的第一門編程語言使用的教材。而本書通篇都是各種
a,b,c,x,y,z 的命名方式。這種poor naming的方式被廣大程序員紛紛效仿,導致如今在很多項目代碼中隨處可見。
– 命名需要一定的英文功底,而國內程序員的英文水平參差不齊。
很多程序員被教育後開始逐漸重視命名,但是受限於英文水平,不知道使用什麼合適的英文詞彙來命名。有的甚至直接把中文直譯為英文的方式命名,或者直接用拼音來命名,反而得不償失。
命名的重要性我想不需要過於強調。如今的軟體開發早已不是求伯君那種單槍匹馬的時代。你寫下的每一行代碼都會在不久的以後被團隊的其他人甚至你自己多次查看。如果是個開源項目,那麼更會被全球各地的人查看源代碼。所以代碼的可讀性就變得尤為重要。如果讀者能夠輕松讀出你的代碼的意圖,那麼就說明你的命名功底相當扎實。
比如在一個管理系統中,你使用這樣的代碼: a = b * c
很容易讓人摸不著頭腦,雖然程序能夠正常運作,但恐怕沒人敢輕易修改這行他們不了解的代碼。而如果修改成為這樣: weeklypay =
hours_worked * pay_rate; 那恐怕極少有人不懂這行代碼的意圖。
糟糕的命名也會導致大量無謂的注釋,這是一個很容易跳進去的陷阱。下一段代碼怕別人不明白你的意圖,那麼就加上注釋。這貌似是一個很精妙的想法,實際上卻南轅北轍。比如以下的注釋:
int d; // elapsed time in days
貌似很容易讓人讀懂,但是問題還是很多。首先注釋不能跟著所有的引用,在定義處了解了d的含義,繼續往下看的話卻很容易忘記;其次代碼更新了,很可能會忘記修改注釋,反而給把讀者帶入歧途。
與其用這樣的注釋,還不如直接重命名: int elapsedTimeInDays; 這樣清晰易懂,還不用維護注釋,何樂而不為?
那麼如何著手來提高的自己的命名技巧那?
首先尋找一份公認的代碼規范,並嚴格按照這樣的標准執行。比如google開源了自己內部使用的語言編碼規范,我們可以直接拿來使用。比如請看Google
Java的style guide,相當詳實。除此之外還有C++等。這里收集了Google對各種語言的編碼規范,非常具有參考價值。
標準的代碼規范中的每一條都是有勝出的理由,值得我們遵從。但某些命名問題不一定只有一種最好的解決方式,這就需要團隊自己建立起約定。比如對於Java單元測試類的命名方式,不同的團隊可能不一樣。比如有的團隊喜歡以should開頭,有的喜歡test開頭,有的喜歡駱駝命名法,有些喜歡下劃線命名法,每種方式有各自的利弊,沒有一種能完全脫穎而出,所以需要團隊自行制定。一旦確定使用某一種,那麼一定要保持一致。
某些命名規范其實是可以進行自動化檢查的,比如在Java應用的構建過程中可以引用checkStyle這款插件,對命名進行一些基本的檢查,比如方法名、變數名是否遵循了一定模式等。這樣在一定程度上可以強制大家遵守某些約定。自己以前曾經寫過一篇文章,請參見這里。
最後要在團隊中建立起code review的機制,通過code
review來相互監督糾正命名問題,並且這樣更容易達成一致的命名約定,方便協作開發。code
review可以採取非正式會議評審的方式。最簡單的方式就是每天找個固定時間大家一起聚在一個顯示器前review每個人的代碼,現場提出問題,當事人記錄下來會後更改。這種方式非常高效。另外有的團隊在嵌入代碼時可能會引入一些代碼評審機制,比如pull
request, cherry pick等。這種review方式比較重量級,反饋周期也較長,好處是可以保證最終遷入的代碼是沒有問題的。
很多語言和框架為了更加可讀,都把命名玩出花來了。比如JavaScript生態圈中重要的單元測試工具Jasmine把測試函數以it命名,這樣可以與參數連接起來成為一種表意的自然語言:
如何優雅地為程序中的變數和函數命名?
- 不同的代碼段採用不同的命名長度。通常來說,循環計數器(loop
counters)採用1位的單字元來命名,循環判斷變數(condition/loop
variables)採用1個單詞來命名,方法採用1-2個單詞命名,類採用2-3個單詞命名,全局變數採用3-4個單詞命名。
- 對變數採用具體的命名(specific names)方式,」value」, 「equals」,
「data」在任何情況下都不是一種有效的命名方式。
- 採用有意義的命名(meaningful names)。變數的名字必須准確反映它的含義和內容。
- 不要用 o_, obj_, m_ 等前綴命名。變數不需要前綴標簽來表示自己是一個變數。
- 遵循公司的變數命名規則,在項目中堅持使用同一種變數命名方式。例如txtUserName, lblUserName,
cmbSchoolType等,否則會對可讀性造成影響,而且會令查找/替換工具(find/replace tools)不可用。
- 遵循當前語言的變數命名規則,不要不統一(inconsistently)地使用大/小寫字母。例如:userName, UserName,
USER_NAME, m_userName, username, …。
以Java為例:
* 類名使用駝峰命名法(Camel Case):VelocityResponseWriter
* 包名使用小寫:com.company.project.ui
* 變數使用首字母小寫的駝峰命名法(Mixed Case):studentName
* 常量使用大寫:MAX_PARAMETER_COUNT = 100
* 枚舉類(enum class)採用駝峰命名法,枚舉值(enum values)採用大寫。
* 除了常量和枚舉值以外,不要使用下劃線』_』
- 在同一個類不同的場景(contexts)中不要復用變數名。例如在方法、初始化方法和類中。這樣做可以提高可讀性和可維護性。
- 不要對不同使用目的的變數使用同一個變數名,而是賦予它們不同的名字。這同樣對保持可讀性和可維護性很重要。
- 變數名不要使用非ASCII字元(non-ASCII chars)。這樣做可能會在跨平台使用時產生問題。
-
不要使用過長的變數名(例如50個字元)。過長的變數名會導致代碼醜陋(ugly)和難以閱讀(hard-to-read),還可能因為字元限制在某些編譯器上存在兼容性問題。
- 僅使用一種自然語言(natural language)來命名變數。例如,同時使用德語和英語來命名變數會導致(理解)不一致和降低可讀性。
- 使用有意義的方法名。方法名必須准確表達該方法的行為,在多數情況下以動詞(verb)開頭。(例如:createPasswordHash)
- 遵循公司的方法命名規則,在項目中堅持使用同一種方法命名方式。例如 getTxtUserName(), getLblUserName(),
isStudentApproved(),否則會對可讀性造成影響,而且會令查找/替換工具不可用。
- 遵循當前語言的變數命名規則,不要不統一地使用大/小寫字母。例如:getUserName, GetUserName, getusername,
…。
以Java為例:
* 方法使用首字母小寫的駝峰命名法:getStudentSchoolType
* 方法參數使用首字母小寫的駝峰命名法:setSchoolName(String schoolName)
- 使用有意義的方法參數命名,這樣做可以在沒有文檔的情況下盡量做到「自解釋(documentate itself)」
總之,命名問題只是整個編碼規范中的一小部分,但是起的作用舉足輕重,它是判斷一個程序員是否專業的必要標准。