c++基礎知識 1-5

1.extern 聲明

基本解釋:extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。此外extern也可用來進行鏈接指定。

      也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目全非,可能是fun@aBc_int_int#%$也可能是別的,這要看編譯器的"脾氣"了(不同的編譯器採用的方法不一樣),爲什麼這麼做呢,因爲C++支持函數的重載啊,在這裏不去過多的論述這個問題,如果你有興趣可以去網上搜索,相信你可以得到滿意的解釋!
    

第二,當extern不與"C"在一起修飾變量或函數時,如在頭文件中: extern int g_Int; 它的作用就是聲明函數或全局變量的作用範圍的關鍵字,其聲明的函數和變量可以在本模塊活其他模塊中使用,記住它是一個聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。

2.float與“零值”比較

假設浮點變量的名字爲x,應當將

if (x == 0.0)         // 隱含錯誤的比較

轉化爲

if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON 是允許的誤差(即精度)。

標準答案示例:

const float EPSINON = 0.00001;//#define EPSINON 1e-6

if ((x >= - EPSINON) && (x <= EPSINON)


3.sizeof操作符  strlen庫函數

首先操作符是什麼,+ - = / %等叫做操作符,sizeof也是操作符,操作符可以怎樣,以string爲例,他會對= +等操作符進行重載,就是函數名字一樣,但是參數列表不同

函數呢,如果名字相同參數相同,函數就是可以被覆蓋


4.malloc  new

1、new 是c++中的操作符,malloc是c 中的一個函數

2、new 不止是分配內存,而且會調用類的構造函數,同理delete會調用類的析構函數,而malloc則只分配內存,不會進行初始化類成員的工作,同樣free也不會調用析構函數

3、內存泄漏對於malloc或者new都可以檢查出來的,區別在於new可以指明是那個文件的那一行,而malloc沒有這些信息。

4.new可以看成兩個動作:1。分配內存(相當於malloc)2。引發構造函數。

new   是個操作符,和什麼"+","-","="...有一樣的地位.

malloc是個分配內存的函數,供你調用的.

new是保留字,不需要頭文件支持.

malloc需要頭文件庫函數支持.

new   建立的是一個對象,

malloc分配的是一塊內存.

new建立的對象你可以把它當成一個普通的對象,用成員函數訪問,不要直接訪問它的地址空間

malloc分配的是一塊內存區域,就用指針訪問好了,而且還可以在裏面移動指針.

5、new 和 malloc效率比較

new 有三個字母, malloc有六個字母

new可以認爲是malloc加構造函數的執行。

new出來的指針是直接帶類型信息的。

而malloc返回的都是void指針。

一:new delete 是運算符,malloc,free是函數

malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。

對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內(構造函數在新建類的時候會被自動調用,在釋放類對象的時候會自動調用析構函數,這在編譯器編譯的時候就會自動在程序中相應的地方加入構造函數和析構函數的代碼,但是malloc和free只是一個外部的庫函數,編譯器不會在編譯的時候在適當的地方自動調用,所以“不能夠把執行構造函數和析構函數的任務強加於malloc/free。”),不能夠把執行構造函數和析構函數的任務強加於malloc/free。

因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。

我們先看一看malloc/free和new/delete如何實現對象的動態內存管理,見示例。


class Obj

{

public :

Obj(void){ cout << “Initialization” << endl; }

~Obj(void){ cout << “Destroy” << endl; }

void      Initialize(void){ cout << “Initialization” << endl; }

void      Destroy(void){ cout << “Destroy” << endl; }

};


void UseMallocFree(void)

{

Obj    *a = (obj *)malloc(sizeof(obj));     // 申請動態內存

a->Initialize();                          // 初始化

//…

a->Destroy();     // 清除工作

free(a);          // 釋放內存

}


void UseNewDelete(void)

{

Obj    *a = new Obj;    // 申請動態內存並且初始化

//…

delete a;             // 清除並且釋放內存

}

示例用malloc/free和new/delete如何實現對象的動態內存管理

類Obj的函數Initialize模擬了構造函數的功能,函數Destroy模擬了析構函數的功能。函數UseMallocFree中,由於malloc/free不能執行構造函數與析構函數,必須調用成員函數Initialize和Destroy來完成初始化與清除工作。函數UseNewDelete則簡單得多。

所以我們不要企圖用malloc/free來完成動態對象的內存管理,應該用new/delete。由於內部數據類型的“對象”沒有構造與析構的過程,對它們而言malloc/free和new/delete是等價的。

既然new/delete的功能完全覆蓋了malloc/free,爲什麼C++不把malloc/free淘汰出局呢?這是因爲C++程序經常要調用C函數,而C程序只能用malloc/free管理動態內存。

如果用free釋放“new創建的動態對象”,那麼該對象因無法執行析構函數而可能導致程序出錯。如果用delete釋放“malloc申請的動態內存”,理論上講程序不會出錯,但是該程序的可讀性很差。所以new/delete必須配對使用,malloc/free也一樣。

二:new delete在實現上其實調用了malloc,free函數。

三:new operator除了分配內存,還要調用構造函數。

malloc函數只是負責分配內存。


5.數組名 a、 &a

C/C++裏面的數組名字會退化爲指針,所以數組名a實際指的是數組的第一個元素的地址。而數組名作爲指針來講有特殊性,它正在它所指向的內存區域中,&a的值和a的數值是相同的(可以輸出觀察一下),但是類型和意義不同。而指針的加法操作和指向的數據類型密切相關。
比如:
int a[10]; a就相當於int *,如果是對它加1(a + 1)是相當於a + 1 * sizeof(int)。但是&a的類型則相當於int **,是所謂指向數組的指針,是數組元素類型的二級指針,對它加1是相當於 &a + 1 * sizeof(a)的,所以會偏移一個數組長度。

6.strcpy,sprintf,memcpy的區別

strcpy,sprintf,memcpy的區別

strcpy 函數操作的對象是 字符串,完成 從 源字符串 到 目的字符串 的 拷貝 功能。

snprintf 函數操作的對象 不限於字符串:雖然目的對象是字符串,但是源對象可以是字符串、也可以是任意基本類型的數據。這個函數主要用來實現 (字符串或基本數據類型)向 字符串 的轉換 功能。如果源對象是字符串,並且指定 %s 格式符,也可實現字符串拷貝功能。

memcpy 函數顧名思義就是 內存拷貝,實現 將一個 內存塊 的內容複製到另一個 內存塊 這一功能。內存塊由其首地址以及長度確定。程序中出現的實體對象,不論是什麼類型,其最終表現就是在內存中佔據一席之地(一個內存區間或塊)。因此,memcpy 的操作對象不侷限於某一類數據類型,或者說可 適用於任意數據類型,只要能給出對象的起始地址和內存長度信息、並且對象具有可操作性即可。鑑於 memcpy 函數等長拷貝的特點以及數據類型代表的物理意義,memcpy 函數通常限於同種類型數據或對象之間的拷貝,其中當然也包括字符串拷貝以及基本數據類型的拷貝。

對於字符串拷貝來說,用上述三個函數都可以實現,但是其實現的效率和使用的方便程度不同:

strcpy 無疑是最合適的選擇:效率高且調用方便。 

snprintf 要額外指定格式符並且進行格式轉化,麻煩且效率不高。 

memcpy 雖然高效,但是需要額外提供拷貝的內存長度這一參數,易錯且使用不便;並且如果長度指定過大的話(最優長度是源字符串長度 + 1),還會帶來性能的下降。其實 strcpy 函數一般是在內部調用 memcpy 函數或者用匯編直接實現的,以達到高效的目的。因此,使用 memcpy 和 strcpy 拷貝字符串在性能上應該沒有什麼大的差別。 

對於非字符串類型的數據的複製來說,strcpy 和 snprintf 一般就無能爲力了,可是對 memcpy 卻沒有什麼影響。但是,對於基本數據類型來說,儘管可以用 memcpy 進行拷貝,由於有賦值運算符可以方便且高效地進行同種或兼容類型的數據之間的拷貝,所以這種情況下 memcpy 幾乎不被使用。memcpy 的長處是用來實現(通常是內部實現居多)對結構或者數組的拷貝,其目的是或者高效,或者使用方便,甚或兩者兼有。

另外,

strcpy和memcpy功能上也有些差別:

比如:

const char *str1="abc/0def";

char str2[7];

首先用strcpy實現:

strcpy(str2,str1

得到結果:str2"abc";也就是說,strcpy是以'/0'爲結束標誌的。

再用memcpy實現:

memset(str2,7);

memcpy(str2,str1,7);

得到結果:str2="abc/0def";

也就是說,memcpy是對內存區域的複製。當然,不僅能夠複製字符串數組,而且能夠複製整型數組等其他數組。


最近在做遠程升級的內容,通過實踐才真正體會到不同拷貝函數的作用

char*strcpy(char *dest, const char *src);

其對字符串進行操作,完成從源字符串到目的字符串的拷貝,當源字符串的大小大於目的字符串的最大存儲空間後,執行該操作會出現段錯誤。

int sprintf(char*str, const char *format, ...)

函數操作的源對象不限於字符串:源對象可以是字符串、也可以是任意基本類型的數據。主要是實現將其他數據類型轉換爲字符串

void *memcpy(void*dest, const void *src, size_t n)

實現內存的拷貝,實現將一塊內存拷貝到另一塊內存。該函數對源對象與目的對象沒有類型現在,只是對內存的拷貝

但是在軟件升級中,當接收到網絡傳送的升級內容後,進行數據拷貝時,最好使用memcpy來進行數據的拷貝。因爲strcpy、sprintf進行拷貝時,當檢查到源字符串中有’\0’即ascii碼爲00)時,即認爲數據結束符,就會停止拷貝



6、typedef char *String_t; 和 #define String_d char * 這兩句在使用上有什麼區別?
答:typedef char *String_t 定義了一個新的類型別名,有類型檢查。而#define String_d char * 只是做了個簡單的替換,無類型檢查,前者在編譯的時候處理,後者在預編譯的時候處理。
同時定義多個變量的時候有區別,主要區別在於這種使用方式String_t  a,b;  String_d  c,d;    a,b ,c都是char*類型,而d爲char類型
由於typedef還要做類型檢查。。#define沒有。。所以typedef比#define安全。。


7.
9、下列關於虛函數的說法正確的是()
A、在構造函數中調用類自己的虛函數,虛函數的動態綁定機制還會生效。
B、在析構函數中調用類自己的虛函數,虛函數的動態綁定機制還會生效。
C、靜態函數不可以是虛函數

因爲靜態成員函數沒有this,也就沒有存放vptr的地方,同時其函數的指針存放也不同於一般的成員函數,其無法成爲一個對象的虛函數的指針以實現由此帶來的動態機制。靜態是編譯時期就必須確定的,虛函數是運行時期確定的。
D、虛函數可以聲明爲inline

inline函數和virtual函數有着本質的區別,inline函數是在程序被編譯時就展開,在函數調用處用整個函數體去替換,而virtual函數是在運行期才能夠確定如何去調用的,因而inline函數體現的是一種編譯期機制,virtual函數體現的是一種運行期機制。
因此,內聯函數是個靜態行爲,而虛函數是個動態行爲,他們之間是有矛盾的。
函數的inline屬性是在編譯時確定的, 然而,virtual的性質則是在運行時確定的,這兩個不能同時存在,只能有一個選擇,文件中聲明inline關鍵字只是對編譯器的建議,編譯器是否採納是編譯器的事情。
我並不否認虛函數也同樣可以用inline來修飾,但你必須使用對象來調用,因爲對象是沒有所謂多態的,多態只面向行爲或者方法,但是C++編譯器,無法保證一個內聯的虛函數只會被對象調用,所以一般來說,編譯器將會忽略掉所有的虛函數的內聯屬性。


相關知識點:什麼函數不能聲明爲虛函數?
一個類中將所有的成員函數都儘可能地設置爲虛函數總是有益的。 
設置虛函數須注意: 
1:只有類的成員函數才能說明爲虛函數; 
2:靜態成員函數不能是虛函數; 
3:內聯函數不能爲虛函數; 
4:構造函數不能是虛函數; 
5:析構函數可以是虛函數,而且通常聲明爲虛函數。



8.
1、已知一段文本有1382個字符,使用了1382個字節進行存儲,這段文本全部是由a、b、c、d、e這5個字符組成,a出現了354次,b出現了483次,c出現了227次,d出現了96次,e出現了232次,對這5個字符使用哈夫曼(Huffman)算法進行編碼,則以下哪些說法正確()
A、使用哈夫曼算法編碼後,用編碼值來存儲這段文本將花費最少的存儲空間
B、使用哈夫曼算法進行編碼,a、b、c、d、e這5個字符對應的編碼值是唯一確定的
C、使用哈夫曼算法進行編碼,a、b、c、d、e這5個字符對應的編碼值可以有多套,但每個字符編碼的位(bit)數是確定的
D、b這個字符的哈夫曼編碼值位數應該最短,d這個字符的哈夫曼編碼值位數應該最長

發佈了2 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章