static在C和C++裏各表明什麼含義?c++
static關鍵字有三種使用方式,其中前兩種只指在C語言中使用,第三種在C++中使用。編程
1. 局部靜態變量(C)數組
2. 外部靜態變量/函數(C)安全
3. 靜態數據成員/成員函數(C++)數據結構
1、 局部靜態變量多線程
局部變量按照存儲形式能夠分爲三種,分別是auto、static、register。ide
與auto類型(普通)局部變量相比,static有三點不一樣:函數
1. 存儲空間分配不一樣工具
auto類型分配在棧上,屬於動態存儲類別,佔動態存儲空間,函數調用結束後自動釋放;優化
static類型分配在靜態存儲區,在程序整個運行期間都不釋放;
二者做用域相同,而且生存期不一樣。
2. static局部變量在初次運行時進行初始化工做,且只初始化一次。
3. 對於局部靜態變量,若是不賦初值,編譯期會自動賦初值0或者空;
auto類型的初值是不肯定的。
對於C++的類對象例外,class的對象實例若是不初始化,則會自動調用默認構造函數,無論是否是static類型。
特色:static局部變量的「記憶性」與生存期的「全局性」
所謂「記憶性」是指在兩次函數調用時,在第二次調用進入時,能保持第一次調用退出時的值。
利用生存期的」全局性「改善return a pointer / reference to a local object的問題,local object的問題在於退出函數時,生存期就結束,局部變量就會被銷燬;利用static就能夠延長局部變量的生存期。
注意事項:
1. 「記憶性」是程序運行很重要的一點就是可重複性,而static變量的「記憶性」破壞了可重複性,形成不一樣時刻同一函數的運行結果不一樣。
2. 「生存期」全局性和惟一性。 普通的局部變量在棧上分配空間,所以每次調用函數時,分配的空間均可能不同,而static具備全局惟一性的特色,每次調用時都指向同一塊內存,這就形成一個很重要的問題---不可重入性!!!
在多線程或者遞歸程序中要特別注意。
2、 外部靜態變量/函數
在C中static的第二種含義:用來表示不能被其它文件訪問的全局變量和函數。
此處static的含義是指對函數的做用域僅僅侷限於本文件(因此又稱爲內部函數)。
注意:對於外部(全局)變量,不管是否有static限制,它的存儲區域都是在靜態存儲區,生存期都是全局的,此時的static只是起做用域限制做用,限制做用域在本文件內部。
使用內部函數的好處是:不一樣的人編寫不一樣的函數時,不用擔憂函數同名問題。
3、 靜態數據成員/成員函數(C++特有)
C++重用了這個關鍵字,它表示屬於一個類而不是屬於此類的任何特定的對象的變量和函數。
靜態類成員包括靜態數據成員和靜態函數成員。
1. 靜態數據成員
類體中的數據成員的聲明前加上static關鍵字,該數據成員就成爲了該類的靜態數據成員。和其餘數據成員同樣,靜態數據成員也遵照public/protected/private訪問規則。同時靜態數據成員還具備如下特色。
1) 靜態數據成員的定義
靜態數據成員其實是類域中的全局變量。因此,靜態數據成員的定義(初始化)不該該被放在頭文件中
注:不要試圖在頭文件中定義(初始化)靜態數據成員。在大多數狀況下,這會引發重複定義。即便加上#ifndef #define #endif或者#pragma once也不行。
2) 靜態數據成員被類的全部對象所共享,包括該類的派生類的對象。
3) 靜態數據成員能夠成爲成員函數的可選參數,而普通數據成員則不能夠。
4)★靜態數據成員的類型能夠是所屬類的類型,而普通數據成員則不能夠。普通數據成員的只能聲明爲所屬類類型的指針或引用。靜態數據成員的值在const成員函數中能夠被合法的改變。
2 靜態成員函數
1).靜態成員函數的地址可用普通函數指針儲存,而普通成員函數地址須要用類成員函數指針來儲存。
2).靜態成員函數不能夠調用類的非靜態成員。由於靜態成員函數不含this指針。
3).靜態成員函數不能夠同時聲明爲 virtual、const、volatile函數。
最後要說的一點是,靜態成員是能夠獨立訪問的,也就是說,無須建立任何對象實例就能夠訪問。
const在C/C++裏有什麼意思?
在C中const的用法:
一、在定義變量時使用(const常量在定義後不能被修改,故在定義時必定要進行初始化操做)
A:變量是個常變量: int const a=10;
const int a=10;
B:指針爲指向常數的指針,(指針自己的值能夠改變)
const int *cur=&b;
C :指針自己值不可變,但指向的內容能夠變
int * const cur=&b;
D :指針爲指向常數的常指針,即指針自己及指針指向的內容都不可變
const int * const cur =&b;
E: 說明引用爲常數引用,即不能改變引用的值
const int& cur = 10;
二、在定義函數時使用
A 做爲參數使用,說明函數體內是不能修改該參數的:
int fun(const int i);
B 返回值使用,說明函數的返回值是不能被修改的:
const int fun();
C 在函數中使用const,狀況與定義變量的狀況基本一致:
在C++中const用法:
A : const類成員變量
在對象構造期間容許被初始化並在之後不容許被改變。const類成員和通常的const變量不一樣,const類成員是對於每一個對象而言,它在對象構造期間被初始化,在這個對象生命期間不容許被改變。
class A
{
…
const int nValue; //成員常量不能被修改
…
A(int x): nValue(x) { } ; //只能在初始化列表中賦值
}
B: const成員函數
const 成員函數不容許在此函數體內對此函數對應的類的全部成員變量進行修改,這樣能夠提升程序的健壯性。通常寫在函數的最後來修飾
class A
{
…
void function()const; //常成員函數, 它不改變對象的成員變量.
//也不能調用類中任何非const成員函數。
}
C: const修飾類對象/對象指針/對象引用
const修飾類對象表示該對象爲常量對象,其中的任何成員都不能被修改。對於對象指針和對象引用也是同樣。
const修飾的對象,該對象的任何非const成員函數都不能被調用,由於任何非const成員函數會有修改爲員變量的企圖。
class AAA
{
void func1();
void func2() const;
}
const AAA aObj;
aObj.func1(); //×
aObj.func2(); //正確
const AAA* aObj = new AAA();
aObj-> func1(); //×
aObj-> func2(); //正確
const在C和C++中最大的不一樣是,在C中,const默認具備外部連接,而C++中則是內部連接。
因此當你只在定義const常量的文件中使用該常量時,c++不給你的const常量分配空間,此時const int c = 0;
至關於#define c 0;而在C中,它會給每一個const 常量分配內存空間。
volatile的做用?
★const和define的區別:
1.Const常量有數據類型,而宏常量沒有數據類型,編譯器能夠對前者進行類型安全的檢查,對後者只進行字符替換,沒有類型安全的檢查,而且在字符替換中產生意料不到的錯誤;
2.有些集成的調試工具能夠對const常量進行調試,可是不能對宏常量進行調試;
★volatile
volatile 影響編譯器編譯的結果,指出volatile 變量是隨時可能發生變化的,與volatile變量有關的運算,不要進行編譯優化,以避免出錯,(VC++ 在產生release版可執行碼時會進行編譯優化,加volatile關鍵字的變量有關的運算,將不進行編譯優化).
例如:
volatile int i=10;
int j = i;
...
int k = i;
volatile 告訴編譯器i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,於是編譯器生成的可執行碼會從新從i的地址讀取數據放在k中。 而 優化作法是,因爲編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操做,它會自動把上次讀的數據放在k中。而不是從新從i裏面讀。這樣以來,若是 i是一個寄存器變量或者表示一個端口數據就容易出錯,因此說volatile能夠保證對特殊地址的穩定訪問,不會出錯。精確地說就是,優化器在用到這個變量時必須每次都當心地從新讀取這個變量的值,而不是使用保存在寄存器裏的備份。
★struct和class的區別:
Struct是public,class是private;
「class」這個關鍵字還用於定義模板參數,就像「typename」。但關鍵字「struct」不用於定義模板參數。
struct更適合當作是一個數據結構的實現體,class更適合當作是一個對象的實現體。
★enum
Enum 枚舉名 {枚舉元素列表}
例如:enum {sun,mon,tus,wed,thu,fri,sat}workday,weekend;
C 編譯對枚舉類型裏的枚舉元素按常量處理,稱爲枚舉常量;每個枚舉元素都表明了一個整數,按定義的順序默認它們的值爲0,1,2,3,4,5,6,……,也能夠人爲的指定值,但後面的元素必須是依次加1;枚舉元素也能夠來用做比較;
★union 共用體或聯合體
union用來維護足夠的空間來放置多個數據成員中的一種,在union中全部的數據共用一個存儲空間,同時間只能存儲其中一個數據成員,也只能用其中的這個數據成員,不能同時被用,因此它起到了一個壓縮空間的做用,全部的成員具備相同的起始地址;一個union只配置一個足夠大的空間來容納最大長度的數據的大小。
★inline
內聯函數和普通函數相比加快了程序的運行速度,由於不須要中斷調用,在編譯的時候內聯函數能夠直接被鑲嵌到目標代碼中,而減小普通函數調用時的資源消耗;
注意事項:在內聯函數內不容許使用循環語句和開關語句,不然按非內聯函數處理;內聯函數的定義必須出如今內聯函數第一次調用以前;
★register 寄存器
速度是最快的!數據從內存中拿出來就放在寄存器中,而後cpu從寄存器中拿數據;
★數組
一維數組:int a[10] 該數組有十個元素,下標爲0—9;
數組名是數組首元素的地址,及a等價於&a[0];
二維數組:
經過指針引用二維數組:
*(a[i]+j)或*(*(a+i)+j)是a[i][j]的值;
a[0]+0,a[0]+1分別是a[0][0],a[0][1]元素的地址(即&a[0][0],&a[0][1]);
*(a[0]+j)指向的是a[0][j]的值;*(a+j)指的是&a[j];
在指向行的指針前加一個*,就轉換爲指向列的指針;在指向列的指針前加&就成爲指向行的指針。
★指針與數組的對比
1.修改內容
示例 7-3-1 中,字符數組 a 的容量是 6 個字符,其內容爲 hello\0。 a 的內容能夠改
變,如 a[0]= ‘X’。指針 p 指向常量字符串「 world」(位於靜態存儲區,內容爲 world\0),
常量字符串的內容是不能夠被修改的。從語法上看,編譯器並不以爲語句 p[0]= ‘X’有什
麼不妥,可是該語句企圖修改常量字符串的內容而致使運行錯誤。
char a[] = 「hello」;
a[0] = ‘X’;
cout << a << endl;
char *p = 「world」; // 注意 p 指向常量字符串
p[0] = ‘X’; // 編譯器不能發現該錯誤
cout << p << endl;
示例 7-3-1 修改數組和指針的內容
2. 內容複製與比較
不能對數組名進行直接複製與比較。示例 7-3-2 中,若想把數組 a 的內容複製給數
組 b,不能用語句 b = a ,不然將產生編譯錯誤。應該用標準庫函數 strcpy 進行復制。
同理, 比較 b 和 a 的內容是否相同, 不能用 if(b==a) 來判斷, 應該用標準庫函數 strcmp
進行比較。
語句 p = a 並不能把 a 的內容複製指針 p,而是把 a 的地址賦給了 p。要想複製 a
的內容,能夠先用庫函數 malloc 爲 p 申請一塊容量爲 strlen(a)+1 個字符的內存,再
用 strcpy 進行字符串複製。同理,語句 if(p==a) 比較的不是內容而是地址,應該用庫
函數 strcmp 來比較。
// 數組…
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能用 b = a;
if(strcmp(b, a) == 0) // 不能用 if (b == a)
…
// 指針…
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); // 不要用 p = a;
if(strcmp(p, a) == 0) // 不要用 if (p == a)
…
示例 7-3-2 數組和指針的內容複製與比較
高質量 C++/C 編程指南, v 1.0
2001 Page 47 of 101
3.計算內存容量
用運算符 sizeof 能夠計算出數組的容量(字節數)。示例 7-3-3( a)中, sizeof(a)
的值是 12(注意別忘了’\0’)。指針 p 指向 a,可是 sizeof(p)的值倒是 4。這是由於
sizeof(p)獲得的是一個指針變量的字節數,至關於 sizeof(char*),而不是 p 所指的內
存容量。 C++/C 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。
注意當數組做爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。示例
7-3-3( b)中,不論數組 a 的容量是多少, sizeof(a)始終等於 sizeof(char *)。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字節
cout<< sizeof(p) << endl; // 4 字節
示例 7-3-3( a) 計算數組和指針的內存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字節而不是
★指針與引用的區別
1.非空區別:在任何狀況下不能使用指向控制的引用,一個引用必須是指向某些對象,所以你若是使用一個變量並讓它指向一個對象,可是該變量在某些時候也不指向任何對象,這是你應該把變量聲明爲指針,由於這樣你能夠賦空值給該變量,相反若是變量指向一個對象,例如你的設計不容許變量爲空,這時你就能夠把變量聲明爲引用。使用引用的代碼效率確定比使用指針的效率高。
2.合法性區別
在使用引用以前不須要檢測其合法性,相反,指針則應該接受檢查,防止其爲空;
3.可修改區別
指針能夠被從新賦值指向另外一個不一樣的對象,但引用老是指向在初始化時被指定的對象,之後不能改變,可是指定的對象其內容能夠改變。
④應用區別
指針:一是你考慮到存在不指向任何對象的可能;二是你須要可以在不一樣的時刻指向不一樣的對象;若是老是指向一個對象,而且一單指向一個對象後就不會改變,那麼你應該使用引用。
★字節對齊
在默認狀況下,爲了方便底結構體內元素的訪問和管理,當結構體內的元素的長度都小於處理器的位數是,便以結構體裏面最長的數據元素對齊單位,也就是說結構體的長度必定是最長數據元素的整數倍,若是結構體內存在長度大於處理器位數的元素,那麼就以處理器的位數爲對齊單位。可是結構體內類型相同的連續元素將在連續的空間內,和數組同樣。
1.總體空間是佔用空間最大的成員的類型所佔字節的整倍數;
2.數據對其原則:內存按結構成員的前後順序排列,當排到該成員變量時,其前面已擺放的空間大小必須是該成員類型大小的整倍數,若是不夠則補齊,以此向後類推;
★Malloc/free和new/delete的區別和聯繫
1.它們都是動態管理內存的入口;
2.malloc/free是c/c++標準的庫函數,new/delete是c++操做符;
3.malloc/free只是動態分配內存空間/釋放空間,而 new/delete除了分配空間還會調用構造函數和析構函數進行初始化與清理;
④malloc/free須要手動計算類型大小且返回值void*,new/delete可本身計算類型的大小,返回對應類型的指針。
★文件管理
打開文件:fopen(文件名,打開方式) 打開方式常見的有「r(讀)」「w(寫)「;
關閉文件:fclose(文件指針)
例如:FILE*fp=fopen(file,」r」) fp就爲文件指針;
if(fp==NULL)打開失敗;若是以寫(w)的方式打開,不存在文件file的話,就會重建file;
fgetc(fp)// 從fp所指向的文件中讀取一個字符;
fputc(ch,fp)把字符ch輸出到fp所指向的文件中;
char*fgets(char*str,int n,FILE*p)//從文件中讀一個長度爲n的字符串存放到str中;
int fputs(char*str,FILE*p)//將str中的字符串輸出到fp所指向的文件中;
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp)
Buffer指的是存放從文件中讀取的數據的存儲區的地址,對於fwrite是要把此地址開始的存儲區中的數據文件向文件輸出;
size:要讀寫的字節數;
count:要讀寫多少個數據項;
fp:FILE類型的指針。
文件位置標記的定位:fseek(文件類型指針,位移量,起始點);
文件起始位置: SEEK_SET 用數字0表明;
文件當前位置: SEEK_CUR 用數字1表明;
文件末位位置: SEEK_END 用數字2表明;
fseek(fp,0,SEEK_SET) // fseek(fp,0,0)文件開始位置;
fseek(fp,100L,0)將文件位置標記向前移動到離文件開頭100個字節處;
fseek(fp,50L,0)將文件位置標記向前移到離當前位置50個字節處;
fseek(fp,-50L,2)將文件位置標記從文件末尾處向後退10個字節;
feof:int feof(FILE*fp) 檢查文件是否結束,遇文件結束符返回非零值,不然返回0;
eof:檢查文件是否結束,遇文件結束,返回1,不然返回0。