static关键字 void和void指针 函数指针
static關(guān)鍵字
1.作用于變量:
?? 用static聲明局部變量-------局部變量指在代碼塊{}內(nèi)部定義的變量,只在代碼塊內(nèi)部有效(作用域),其缺省的存儲方式是自動變量或說是動態(tài)存儲的,即指令執(zhí)行到變量定義處時(shí)才給變量分配存儲單元,跳出代碼塊時(shí)釋放內(nèi)存單元(生命期)。用static聲明局部變量時(shí),則改變變量的存儲方式(生命期),使變量成為靜態(tài)的局部變量,即編譯時(shí)就為變量分配內(nèi)存,直到程序退出才釋放存儲單元。這樣,使得該局部變量有記憶功能,可以記憶上次的數(shù)據(jù),不過由于仍是局部變量,因而只能在代碼塊內(nèi)部使用(作用域不變)。
?? 用static聲明外部變量-------外部變量指在所有代碼塊{}之外定義的變量,它缺省為靜態(tài)變量,編譯時(shí)分配內(nèi)存,程序結(jié)束時(shí)釋放內(nèi)存單元。同時(shí)其作用域很廣,整個(gè)文件都有效甚至別的文件也能引用它。為了限制某些外部變量的作用域,使其只在本文件中有效,而不能被其他文件引用,可以用static關(guān)鍵字對其作出聲明。
總結(jié):用static聲明局部變量,使其變?yōu)殪o態(tài)存儲方式,作用域不變;用static聲明外部變量,其本身就是靜態(tài)變量,這只會改變其連接方式,使其只在本文件內(nèi)部有效,而其他文件不可連接或引用該變量。
2.作用于函數(shù):
使用static用于函數(shù)定義時(shí),對函數(shù)的連接方式產(chǎn)生影響,使得函數(shù)只在本文件內(nèi)部有效,對其他文件是不可見的。這樣的函數(shù)又叫作靜態(tài)函數(shù)。使用靜態(tài)函數(shù)的好處是,不用擔(dān)心與其他文件的同名函數(shù)產(chǎn)生干擾,另外也是對函數(shù)本身的一種保護(hù)機(jī)制。
如果想要其他文件可以引用本地函數(shù),則要在函數(shù)定義時(shí)使用關(guān)鍵字extern,表示該函數(shù)是外部函數(shù),可供其他文件調(diào)用。另外在要引用別的文件中定義的外部函數(shù)的文件中,使用extern聲明要用的外部函數(shù)即可。
參考資料:
①《?C程序設(shè)計(jì)(第二版)?》,譚浩強(qiáng)
②《?Pointers on C?》,Kenneth A.Reek
void和void指針
void的含義
??? void即“無類型”,void *則為“無類型指針”,可以指向任何數(shù)據(jù)類型。
void指針使用規(guī)范
????①void指針可以指向任意類型的數(shù)據(jù),亦即可用任意數(shù)據(jù)類型的指針對void指針賦值。例如:
??? int *pint;
??? void *pvoid;
??? pvoid = pint;?? /* 不過不能 pint = pvoid; */
??? 如果要將pvoid賦給其他類型指針,則需要強(qiáng)制類型轉(zhuǎn)換如:pint = (int *)pvoid;
????
??? ②在ANSI C標(biāo)準(zhǔn)中,不允許對void指針進(jìn)行算術(shù)運(yùn)算如pvoid++或pvoid+=1等,而在GNU中則允許,因?yàn)樵谌笔∏闆r下,GNU認(rèn)為void *與char *一樣。sizeof( *pvoid )== sizeof( char ).
???
void的作用
????①對函數(shù)返回的限定。
??? ②對函數(shù)參數(shù)的限定。
??? 當(dāng)函數(shù)不需要返回值時(shí),必須使用void限定。例如: void func(int, int);
??? 當(dāng)函數(shù)不允許接受參數(shù)時(shí),必須使用void限定。例如: int func(void)。
??? 由于void指針可以指向任意類型的數(shù)據(jù),亦即可用任意數(shù)據(jù)類型的指針對void指針賦值,因此還可以用void指針來作為函數(shù)形參,這樣函數(shù)就可以接受任意數(shù)據(jù)類型的指針作為參數(shù)。例如:
??? void * memcpy( void *dest, const void *src, size_t len );
??? void * memset( void * buffer, int c, size_t num );
參考資料:《 C/C++語言void及void指針深層探索 》,宋寶華
函數(shù)指針
函數(shù)指針是什么?
??? 先來看函數(shù)調(diào)用是怎么回事。一個(gè)函數(shù)占用一段連續(xù)內(nèi)存。當(dāng)調(diào)用一個(gè)函數(shù)時(shí),實(shí)際上是跳轉(zhuǎn)到函數(shù)入口地址,執(zhí)行函數(shù)體的代碼,完成后返回。如何找到對應(yīng)的入口地址?這是由函數(shù)名來標(biāo)記的,實(shí)際上,函數(shù)名就是函數(shù)的入口地址。
??? 函數(shù)指針是一種特殊類型的指針,它指向一個(gè)函數(shù)的入口地址。
????注意:除了void類型指針是無類型的指針外,其他所有指針都是有對應(yīng)類型的,例如int *pint、struct studentdata *psdata等,只有指明了指針?biāo)傅臄?shù)據(jù)類型,編譯器才能為指針分配或預(yù)計(jì)分配相應(yīng)大小的存儲空間,指針的算術(shù)運(yùn)算如pint++等才是有意義的。因此,定義了某種類型的指針之后,除非使用強(qiáng)制類型轉(zhuǎn)換,那么它只能指向相應(yīng)數(shù)據(jù)類型的變量或常量,不同類型的指針或數(shù)據(jù)之間不可混用。所以指針的類型實(shí)際上是一種身份標(biāo)志的作用。
?? 函數(shù)指針如何表明自己的身份呢?為了避免混亂,必須也要作出相應(yīng)規(guī)定,不同函數(shù)的函數(shù)指針不能混用。例如,int func1(int arg11, char arg12)與int func2(char arg)的函數(shù)指針就不能混用,要定義可以指向func1的函數(shù)指針應(yīng)該這樣:
??? int (*pfunc1)(int, char) = func1;
定義可以指向func2的函數(shù)指針則該如下:
??? int (*pfunc2)(char) = func2;
????從函數(shù)指針的定義可以看出,函數(shù)指針的類型實(shí)際上是由函數(shù)簽名決定的。函數(shù)簽名就象是函數(shù)的身份證,一個(gè)函數(shù)的函數(shù)簽名是獨(dú)一無二的,具有相同函數(shù)簽名的函數(shù)實(shí)際上就是同一函數(shù)。函數(shù)簽名包括函數(shù)名、函數(shù)形參類型的有序列表和函數(shù)返回值類型。
??? 一個(gè)函數(shù)指針的定義規(guī)定了它只能指向特定類型的函數(shù)。如果兩個(gè)函數(shù)的形參列表和返回值類型相同,只有函數(shù)名和函數(shù)體不同,則可以使用相同類型的函數(shù)指針。例如,如果還有一個(gè)函數(shù)int func3(char arg),則上面定義的可以指向函數(shù)func2的函數(shù)指針也可以用于指向func3,即:
??? pfunc2 = func3;
??? 再使用pfunc2(char ARG)就可以調(diào)用函數(shù)func3,這時(shí)指令計(jì)數(shù)器(PC)指向函數(shù)入口,從此開始執(zhí)行函數(shù)體代碼。
??? 注意:對函數(shù)指針進(jìn)行算術(shù)運(yùn)算也是沒有意義的。
如何使用函數(shù)指針?
??? ①定義合適類型的函數(shù)指針變量;
??? ?? int (*pfunc)(int, int);
??? ②給函數(shù)指針變量賦值,使它指向某個(gè)函數(shù)入口;
??? ?? int example(int, int);
??? ?? pfunc = example;?? ??? ?? /*將函數(shù)入口地址賦給函數(shù)指針變量*/
??? ?? 或者:pfunc = &example;??? /*函數(shù)名總是被編譯器轉(zhuǎn)換為函數(shù)指針(入口地址)來使用,因此與上面一句等價(jià) */
??? ③使用函數(shù)指針來調(diào)用相應(yīng)的函數(shù);
??? ?? retval = pfunc(10, 16);
??? ?? 或者:retval = (*pfunc)(10, 16);
??? ?? 上面兩句都與retval = example(10, 16);等價(jià)。
??? 理解:一個(gè)指針變量p實(shí)際上也和普通的變量一樣,要占存儲空間(通常與平臺的虛擬地址一樣寬),也有其自身的存儲地址&p;不同的是,在指針變量p的值有特殊的意義,它是另外一個(gè)變量或常量的地址值,也就是說,在地址為&p的存儲單元上存放著另外一個(gè)數(shù)據(jù)的地址。因此,*p實(shí)際上是將p看作它指向的數(shù)據(jù)的地址來使用,*操作符是引用相應(yīng)地址中的數(shù)據(jù),也就是對地址為p的存儲單元中存放的數(shù)據(jù)進(jìn)行操作。
??? 一個(gè)函數(shù)指針變量則更為特殊。比如上面的例子,pfunc變量本身的值是函數(shù)example()的入口地址。因此pfunc可以代替其所指函數(shù)的函數(shù)名來使用。至于*pfunc,如果按照上面的理解,它實(shí)際上是地址pfunc的內(nèi)容,也即函數(shù)example()的入口地址的內(nèi)容,就有點(diǎn)含糊了。不過,從另一方面來理解,如果使用pfunc = &example來初始化pfunc,則*pfunc == *(&example) == example,又與pfunc等價(jià)。因此,就有了兩種使用函數(shù)指針來調(diào)用相應(yīng)函數(shù)的形式。
??? 值得注意的是,不可用*pfunc來對pfunc的值初始化。即*pfunc = example的寫法是錯(cuò)誤的。
為什么要使用函數(shù)指針?
????前面介紹了函數(shù)指針的基本知識和使用規(guī)范。下面介紹函數(shù)指針的實(shí)際用途。不過首先要對前面的知識再做一個(gè)補(bǔ)充,因?yàn)橄旅娴膽?yīng)用很可能用到這一特性。前面指出,除函數(shù)名之外的函數(shù)簽名內(nèi)容(函數(shù)返回值類型和形參列表)決定了函數(shù)指針的類型。實(shí)際上還有一種特殊的或說通用的函數(shù)指針,在定義這類函數(shù)指針時(shí),只需要指定函數(shù)返回值類型,而留空形參列表,這樣就可以指向返回值類型相同的所有函數(shù)。例如:
??? int (*pfunc)();
??? 這樣定義的pfunc就可以指向前面提到的func1和func2,因?yàn)樗麄兌挤祷卣椭怠?br />????注意:?int (*pfunc)()與int (*pfunc)(void)不是一回事,后者不允許接受任何參數(shù)。
????函數(shù)指針最常見的三個(gè)用途是:
??? ①作為參數(shù)傳遞給其他函數(shù)。
??? 這樣可以把多個(gè)函數(shù)用一個(gè)函數(shù)體封裝起來,得到一個(gè)具有多個(gè)函數(shù)功能的新函數(shù),根據(jù)傳遞的函數(shù)指針變量值的不同,執(zhí)行不同的函數(shù)功能。這是函數(shù)嵌套調(diào)用難以實(shí)現(xiàn)的。參數(shù)的傳遞可以由程序員設(shè)定,也可以由用戶輸入讀取,因此具有較大的靈活性和交互性。
??? 另外還可以用于回調(diào)函數(shù)。使用void配合,還可以將對不同數(shù)據(jù)類型的數(shù)據(jù)進(jìn)行相同處理的多個(gè)函數(shù)封裝為一個(gè)函數(shù),增強(qiáng)函數(shù)的生命力。
??? ②用于散轉(zhuǎn)程序。
??? 這種程序首先建立一個(gè)函數(shù)表(實(shí)際上是一個(gè)函數(shù)指針數(shù)組),表中存放了各個(gè)函數(shù)的入口地址(或函數(shù)名),根據(jù)條件的設(shè)定來查表選擇執(zhí)行相應(yīng)的函數(shù)。這樣也可以將多個(gè)函數(shù)封裝為一個(gè)函數(shù)或者程序,散轉(zhuǎn)分支條件可以由程序員設(shè)定,也可以由用戶輸入讀取,甚至是外設(shè)的某種特定狀態(tài)(這種狀態(tài)可以是不受人為控制的)。
??? ③實(shí)現(xiàn)C的面向?qū)ο蟮念惖姆庋b。
??? C語言中的struct與C++中的class有很大不同,除了缺省的成員屬性外(struct的成員缺省為public的,可隨意使用,而class成員缺省為private的),struct還很難實(shí)現(xiàn)類成員函數(shù)的封裝。struct的成員一般都是數(shù)據(jù)成員,而非函數(shù)成員。因此,為了在C語言中,為某個(gè)struct定義一套自己的函數(shù)對結(jié)構(gòu)數(shù)據(jù)成員進(jìn)行操作,可以在struct結(jié)構(gòu)體中增加函數(shù)指針變量成員,在初始化時(shí)使它指向特定函數(shù)即可。
??? 應(yīng)用舉例:
??? ①假設(shè)定義了四個(gè)函數(shù):add(int, int)、sub(int, int)、mul(int, int)、div(int, int),可以將其封裝為一個(gè)四則運(yùn)算計(jì)算器函數(shù):
??? double calculator(int x, int y, int (*pfunc)(int, int)) {
??? ?? double result;
??? ?? result = pfunc(x, y);
??? ?? return result;
??? }
??? 又例如,在一個(gè)鏈表查詢程序中,要通過比較節(jié)點(diǎn)的特征值來查詢節(jié)點(diǎn),不同類型的數(shù)據(jù)的比較方式不一樣,整型等可以直接比較,字符串卻要用專門的字符串操作函數(shù),為了使代碼可重用性更高,可以使用一個(gè)比較函數(shù)來代替各種不同數(shù)據(jù)類型的直接比較代碼,同時(shí),比較函數(shù)也必然是數(shù)據(jù)類型相關(guān)的,因此要使用void指針和函數(shù)指針來轉(zhuǎn)換為類型無關(guān)的比較函數(shù),根據(jù)相應(yīng)的數(shù)據(jù)類型,調(diào)用相應(yīng)的函數(shù)(傳遞相應(yīng)的函數(shù)指針)。一個(gè)實(shí)例是:
??? int (*compare)(void const *, void const *);
??? 這個(gè)函數(shù)指針可以接受任意類型的數(shù)據(jù)的指針參數(shù),同時(shí)返回int值作為比較結(jié)果標(biāo)志。一個(gè)比較整型數(shù)據(jù)的比較函數(shù)是:
??? int compare_ints(void const *a, void const *b) {
??? ?? if( *(int *)a == *(int *)b )?
??? ?? ?? return 0;
??? ?? else
??? ?? ?? return 1;
??? }
??? ②散轉(zhuǎn)程序。通過一個(gè)轉(zhuǎn)移表(函數(shù)指針數(shù)組)來實(shí)現(xiàn)。還是上面定義的四個(gè)四則運(yùn)算函數(shù),可以建立這樣一個(gè)轉(zhuǎn)移表(注意初始化該轉(zhuǎn)移表的語句前面應(yīng)有add等相應(yīng)函數(shù)原型聲明或定義):
??? double (*calculator[])(int, int) = {
??? ?? add, sub, mul, div
??? };
??? 這樣,calculator[0] == add, calculator[1] == sub, ...
??? 使用result = calculator[oper](x, y);就可以代替下面整個(gè)switch語句:
??? switch( oper ) {
??? ?? case 0: result = add(x, y); break;
??? ?? case 1: result = sub(x, y); break;
??? ?? ...
??? }
??? ③C的面向?qū)ο蠡?。一個(gè)對象包括數(shù)據(jù)和對數(shù)據(jù)的操作。C語言中的struct只有數(shù)據(jù)成員,因此要增加一些“偽數(shù)據(jù)成員”即函數(shù)指針來實(shí)現(xiàn)對數(shù)據(jù)的操作。例如:
??? #ifndef C_Class
??? #define?C_Class struct
??? #endif
????C_Class student{
??? ?? C_Class student *student_this
???? char name;
??? ?? int height;
??? ?? int gender;
??? ?? int classnum;
??? ?? ...
??? ?? void (*Oper)( C_Class student *student_this );
???? ...
??? }
參考資料:
①《 Pointers on C 》,Kenneth A.Reek
②《 C程序設(shè)計(jì)(第二版)》,譚浩強(qiáng)
③《 C語言嵌入式系統(tǒng)編程修煉之道 》
errno與錯(cuò)誤處理
errno是什么?
在/usr/include/errno.h中,include了<sys/errno.h>,在該文件中定義了不同的errno的值(錯(cuò)誤類型編號)所對應(yīng)的宏以及錯(cuò)誤類型.
基本使用:
#include <errno.h>
extern int errno;
1.使用perror( const char *msg )函數(shù)來將錯(cuò)誤類型所對應(yīng)的錯(cuò)誤信息以字符串形式打印到終端.
首先輸出用戶自定義的字符串msg(可以為空,即""),然后打印錯(cuò)誤信息.
2.使用stderr( int errnum )將錯(cuò)誤信息轉(zhuǎn)換為字符串.
3.注意,必須在函數(shù)表明操作失敗后立刻對errno的值進(jìn)行檢查以找出對應(yīng)錯(cuò)誤.在使用它之前必須總是先將其值copy到另外一個(gè)變量保存起來,因?yàn)楹芏嗪瘮?shù)(象fprintf之類)自身就可能會改變errno的值.
func( );
errortype = errno;
printf( "%d/n", errortype );
或者:
if( errortype == ... ) {
?? do ...
}
else {
?? do ....
}
本文轉(zhuǎn)載自http://hi.baidu.com/finalspeed/blog/item/d457747245dfe6178701b0b7.html
總結(jié)
以上是生活随笔為你收集整理的static关键字 void和void指针 函数指针的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019年研究生数学建模竞赛优秀论文汇总
- 下一篇: 莫烦python学习笔记1