C和C++

姓名:王重月? 學(xué)號(hào):21021211019? ?學(xué)院:電子工程學(xué)院

轉(zhuǎn)自:(30條消息) 嵌入式必備知識(shí)_Oliver.H的博客-CSDN博客_嵌入式基本知識(shí)必備

【嵌牛導(dǎo)讀】介紹C和C++的區(qū)別,概念等

【嵌牛鼻子】C和C++

【嵌牛提問】C和C++的區(qū)別到底有哪里不一樣?

【嵌牛正文】

2.1 c和c++區(qū)別、概念相關(guān)面試題

2.1.1 new和malloc的區(qū)別

屬性:

new/delete是C++關(guān)鍵字,需要編譯器支持。malloc/free是庫(kù)函數(shù),需要頭文件支持。

特征:

2.1.2 malloc的底層實(shí)現(xiàn)

Linux維護(hù)一個(gè)break指針,這個(gè)指針指向堆空間的某個(gè)地址。從堆起始地址到break之間的地址空間為映射好的,可以供進(jìn)程訪問;而從break往上,是未映射的地址空間,如果訪問這段空間則程序會(huì)報(bào)錯(cuò)。我們用malloc進(jìn)行內(nèi)存分配就是從break往上進(jìn)行的。 獲得了break指針的位置也就得到了堆內(nèi)存申請(qǐng)的起始地址。

malloc實(shí)際上是將可用空間用一個(gè)空閑鏈表連接起來,若用戶申請(qǐng)空間,就遍歷該鏈表,找到第一個(gè)滿足條件的空閑塊,將其進(jìn)行拆分,返回合適大小的空間給用戶,將剩下的部分鏈接到鏈表中。當(dāng)調(diào)用free釋放空間時(shí),會(huì)把這塊空間連接到空閑鏈表上。到最后,該空閑鏈就會(huì)被切成很多的小內(nèi)存塊,一旦用戶申請(qǐng)一塊較大的空間,空閑鏈中的空間大小都無法滿足需求,malloc會(huì)申請(qǐng)延時(shí),對(duì)空閑鏈表進(jìn)行檢查,內(nèi)存重新整理,把相鄰的小片段合并成大的空閑塊。

2.1.3 在1G內(nèi)存的計(jì)算機(jī)中能否malloc(1.2G)?為什么?

malloc能夠申請(qǐng)的空間大小與物理內(nèi)存的大小沒有直接關(guān)系,僅與程序的虛擬地址空間相關(guān)。程序運(yùn)行時(shí),堆空間只是程序向操作系統(tǒng)申請(qǐng)劃出來的一大塊虛擬地址空間。應(yīng)用程序通過malloc申請(qǐng)空間,得到的是在虛擬地址空間中的地址,之后程序運(yùn)行所提供的物理內(nèi)存是由操作系統(tǒng)完成的。

2.1.4 指針與引用的相同和區(qū)別;如何相互轉(zhuǎn)換?

共性:

1)都是地址的概念,指針指向某一內(nèi)存、它的內(nèi)容是所指內(nèi)存的地址;引用則是某塊內(nèi)存的別名。

2)從內(nèi)存分配上看:兩者都占內(nèi)存,程序?yàn)橹羔槙?huì)分配內(nèi)存,一般是4個(gè)字節(jié);而引用的本質(zhì)是指針常量,指向?qū)ο蟛荒茏?,但指向?qū)ο蟮闹悼梢宰儭烧叨际堑刂犯拍睿员旧矶紩?huì)占用內(nèi)存。

區(qū)別:

1)引用必須被初始化,指針不必。

2)引用初始化以后不能被改變,指針可以改變所指的對(duì)象。

3)不存在指向空值的引用,但是存在指向空值的指針。

轉(zhuǎn)換:

指針轉(zhuǎn)引用:把指針用*就可以轉(zhuǎn)換成對(duì)象,可以用在引用參數(shù)當(dāng)中。

引用轉(zhuǎn)指針:把引用類型的對(duì)象用&取地址就獲得指針了。

2.1.5 C語言檢索內(nèi)存情況 內(nèi)存分配的方式

一個(gè)程序本質(zhì)上都是由BSS段、data段、text段三個(gè)組成的??梢钥吹揭粋€(gè)可執(zhí)行程序在存儲(chǔ)(沒有調(diào)入內(nèi)存)時(shí)分為代碼段、數(shù)據(jù)區(qū)和未初始化數(shù)據(jù)區(qū)三部分。

BSS段(未初始化數(shù)據(jù)區(qū)):通常用來存放程序中未初始化的全局變量和靜態(tài)變量的一塊內(nèi)存區(qū)域。BSS段屬于靜態(tài)分配,程序結(jié)束后靜態(tài)變量資源由系統(tǒng)自動(dòng)釋放。

數(shù)據(jù)段:存放程序中已初始化的全局變量的一塊內(nèi)存區(qū)域。數(shù)據(jù)段也屬于靜態(tài)內(nèi)存分配

代碼段:存放程序執(zhí)行代碼的一塊內(nèi)存區(qū)域。這部分區(qū)域的大小在程序運(yùn)行前就已經(jīng)確定,并且內(nèi)存區(qū)域?qū)儆谥蛔x。在代碼段中,也有可能包含一些只讀的常數(shù)變量

text段和data段在編譯時(shí)已經(jīng)分配了空間,而BSS段并不占用可執(zhí)行文件的大小,它是由鏈接器來獲取內(nèi)存的。

bss段(未進(jìn)行初始化的數(shù)據(jù))的內(nèi)容并不存放在磁盤上的程序文件中。其原因是內(nèi)核在程序開始運(yùn)行前將它們?cè)O(shè)置為0。需要存放在程序文件中的只有正文段和初始化數(shù)據(jù)段。

data段(已經(jīng)初始化的數(shù)據(jù))則為數(shù)據(jù)分配空間,數(shù)據(jù)保存到目標(biāo)文件中。

數(shù)據(jù)段包含經(jīng)過初始化的全局變量以及它們的值。BSS段的大小從可執(zhí)行文件中得到,然后鏈接器得到這個(gè)大小的內(nèi)存塊,緊跟在數(shù)據(jù)段的后面。當(dāng)這個(gè)內(nèi)存進(jìn)入程序的地址空間后全部清零。包含數(shù)據(jù)段和BSS段的整個(gè)區(qū)段此時(shí)通常稱為數(shù)據(jù)區(qū)。

可執(zhí)行程序在運(yùn)行時(shí)又多出兩個(gè)區(qū)域:棧區(qū)和堆區(qū)。

**棧區(qū):**由編譯器自動(dòng)釋放,存放函數(shù)的參數(shù)值、局部變量等。每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),該函數(shù)的返回類型和一些調(diào)用的信息被存放到棧中。然后這個(gè)被調(diào)用的函數(shù)再為他的自動(dòng)變量和臨時(shí)變量在棧上分配空間。每調(diào)用一個(gè)函數(shù)一個(gè)新的棧就會(huì)被使用。棧區(qū)是從高地址位向低地址位增長(zhǎng)的,是一塊連續(xù)的內(nèi)存區(qū)域,最大容量是由系統(tǒng)預(yù)先定義好的,申請(qǐng)的??臻g超過這個(gè)界限時(shí)會(huì)提示溢出,用戶能從棧中獲取的空間較小。

**堆區(qū):**用于動(dòng)態(tài)分配內(nèi)存,位于BSS和棧中間的地址區(qū)域。由程序員申請(qǐng)分配和釋放。堆是從低地址位向高地址位增長(zhǎng),采用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。頻繁的malloc/free造成內(nèi)存空間的不連續(xù),產(chǎn)生碎片。當(dāng)申請(qǐng)堆空間時(shí)庫(kù)函數(shù)是按照一定的算法搜索可用的足夠大的空間。因此堆的效率比棧要低的多。

2.1.6 extern”C” 的作用

extern "C"的主要作用就是為了能夠正確實(shí)現(xiàn)C++代碼調(diào)用其他C語言代碼。加上extern "C"后,會(huì)指示編譯器這部分代碼按C語言的進(jìn)行編譯,而不是C++的。由于C++支持函數(shù)重載,因此編譯器編譯函數(shù)的過程中會(huì)將函數(shù)的參數(shù)類型也加到編譯后的代碼中,而不僅僅是函數(shù)名;而C語言并不支持函數(shù)重載,因此編譯C語言代碼的函數(shù)時(shí)不會(huì)帶上函數(shù)的參數(shù)類型,一般只包括函數(shù)名。

2.1.7頭文件聲明時(shí)加extern定義時(shí)不要加 因?yàn)閑xtern可以多次聲明,但只有一個(gè)定義

2.1.8函數(shù)參數(shù)壓棧順序,即關(guān)于stdcall和cdecl調(diào)用方式的理解

stdcall和cdecl都是函數(shù)調(diào)用約定關(guān)鍵字:

stdcall:參數(shù)由右向左壓入堆棧;堆棧由函數(shù)本身清理。

cdecl:參數(shù)也是由右向左壓入堆棧;但堆棧由調(diào)用者清理。

2.1.9重寫memcpy()函數(shù)需要注意哪些問題

2.1.10數(shù)組到底存放在哪里

2.1.11 struct和class的區(qū)別

相同點(diǎn):

struct能包含成員函數(shù)嗎? 能!

struct能繼承嗎? 能??!

struct能實(shí)現(xiàn)多態(tài)嗎? 能!??!

不同點(diǎn):

(1)關(guān)于使用大括號(hào)初始化

class和struct如果定義了構(gòu)造函數(shù),就不能用大括號(hào)進(jìn)行初始化了;若沒有定義,struct可以用大括號(hào)初始化,而class只有在所有成員變量全是public的情況下,才可以用大括號(hào)進(jìn)行初始化

(2)關(guān)于默認(rèn)權(quán)限訪問

class中默認(rèn)成員訪問權(quán)限是private,而struct的默認(rèn)訪問權(quán)限是public

(3)關(guān)于繼承方式

class中默認(rèn)繼承方式是private,而struct的默認(rèn)繼承方式是public,具體代碼如下

2.1.12 char和int之間的轉(zhuǎn)換

將字符char類型轉(zhuǎn)換成int整型

將字符串轉(zhuǎn)化成int

將int整型轉(zhuǎn)化成字符串型

2.1.13 static的用法(定義和用途)

全局靜態(tài)變量

在全局變量前加上關(guān)鍵字static,全局變量就定義成一個(gè)全局靜態(tài)變量.

靜態(tài)存儲(chǔ)區(qū),在整個(gè)程序運(yùn)行期間一直存在。

初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被自動(dòng)初始化為0(自動(dòng)對(duì)象的值是任意的,除非他被顯式初始化);

作用域:全局靜態(tài)變量在聲明他的文件之外是不可見的,準(zhǔn)確地說是從定義之處開始,到文件結(jié)尾。

局部靜態(tài)變量

在局部變量之前加上關(guān)鍵字static,局部變量就成為一個(gè)局部靜態(tài)變量。

內(nèi)存中的位置:靜態(tài)存儲(chǔ)區(qū)

初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被自動(dòng)初始化為0(自動(dòng)對(duì)象的值是任意的,除非他被顯式初始化);

作用域:作用域仍為局部作用域,當(dāng)定義它的函數(shù)或者語句塊結(jié)束的時(shí)候,作用域結(jié)束。但是當(dāng)局部靜態(tài)變量離開作用域后,并沒有銷毀,而是仍然駐留在內(nèi)存當(dāng)中,只不過我們不能再對(duì)它進(jìn)行訪問,直到該函數(shù)再次被調(diào)用,并且值不變;

靜態(tài)函數(shù)

在函數(shù)返回類型前加static,函數(shù)就定義為靜態(tài)函數(shù)。函數(shù)的定義和聲明在默認(rèn)情況下都是extern的,但靜態(tài)函數(shù)只是在聲明他的文件當(dāng)中可見,不能被其他文件所用。

函數(shù)的實(shí)現(xiàn)使用static修飾,那么這個(gè)函數(shù)只可在本cpp內(nèi)使用,不會(huì)同其他cpp中的同名函數(shù)引起沖突;

warning:不要再頭文件中聲明static的全局函數(shù),不要在cpp內(nèi)聲明非static的全局函數(shù),如果你要在多個(gè)cpp中復(fù)用該函數(shù),就把它的聲明提到頭文件里去,否則cpp內(nèi)部聲明需加上static修飾;

類的靜態(tài)成員

在類中,靜態(tài)成員可以實(shí)現(xiàn)多個(gè)對(duì)象之間的數(shù)據(jù)共享,并且使用靜態(tài)數(shù)據(jù)成員還不會(huì)破壞隱藏的原則,即保證了安全性。因此,靜態(tài)成員是類的所有對(duì)象中共享的成員,而不是某個(gè)對(duì)象的成員。對(duì)多個(gè)對(duì)象來說,靜態(tài)數(shù)據(jù)成員只存儲(chǔ)一處,供所有對(duì)象共用

類的靜態(tài)函數(shù)

靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對(duì)象成員。因此,對(duì)靜態(tài)成員的引用不需要用對(duì)象名。

在靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員(這點(diǎn)非常重要)。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時(shí),可通過對(duì)象來引用。從中可看出,調(diào)用靜態(tài)成員函數(shù)使用如下格式:<類名>::<靜態(tài)成員函數(shù)名>(<參數(shù)表>);

2.1.15const常量和#define的區(qū)別(編譯階段、安全性、內(nèi)存占用等)

編譯器處理不同

宏定義是一個(gè)“編譯時(shí)”概念,在預(yù)處理階段展開(在編譯時(shí)把所有用到宏定義值的地方用宏定義常量替換),不能對(duì)宏定義進(jìn)行調(diào)試,生命周期結(jié)束于編譯時(shí)期;

const常量是一個(gè)“運(yùn)行時(shí)”概念,在程序運(yùn)行使用,類似于一個(gè)只讀行數(shù)據(jù)

存儲(chǔ)方式不同

宏定義是直接替換,不會(huì)分配內(nèi)存,存儲(chǔ)與程序的代碼段中;

const常量需要進(jìn)行內(nèi)存分配

類型和安全檢查不同

宏定義是字符替換,沒有數(shù)據(jù)類型的區(qū)別,同時(shí)這種替換沒有類型安全檢查,可能產(chǎn)生邊際效應(yīng)等錯(cuò)誤;

const常量是常量的聲明,有類型區(qū)別,需要在編譯階段進(jìn)行類型檢查

2.1.16 volatile作用和用法

作用:

Volatile意思是“易變的”,應(yīng)該解釋為“直接存取原始內(nèi)存地址”比較合適。 “易變”是因?yàn)橥庠谝蛩匾鸬?,像多線程,中斷等;

C語言書籍這樣定義volatile關(guān)鍵字:volatile提醒編譯器它后面所定義的變量隨時(shí)都有可能改變,因此編譯后的程序每次需要存儲(chǔ)或讀取這個(gè)變量的時(shí)候,告訴編譯器對(duì)該變量不做優(yōu)化,都會(huì)直接從變量?jī)?nèi)存地址中讀取數(shù)據(jù),從而可以提供對(duì)特殊地址的穩(wěn)定訪問。。如果沒有volatile關(guān)鍵字,則編譯器可能優(yōu)化讀取和存儲(chǔ),可能暫時(shí)使用寄存器中的值,如果這個(gè)變量由別的程序更新了的話,將出現(xiàn)不一致的現(xiàn)象。(簡(jiǎn)潔的說就是:volatile關(guān)鍵詞影響編譯器編譯的結(jié)果,用volatile聲明的變量表示該變量隨時(shí)可能發(fā)生變化,與該變量有關(guān)的運(yùn)算,不要進(jìn)行編譯優(yōu)化,以免出錯(cuò))

下面是volatile變量的幾個(gè)例子:

1). 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)

2). 一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automatic variables)

3). 多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量

回答不出這個(gè)問題的人是不會(huì)被雇傭的。我認(rèn)為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內(nèi)容將會(huì)帶來災(zāi)難。

幾個(gè)問題:

  1)一個(gè)參數(shù)既可以是const還可以是volatile嗎?

  可以的,例如只讀的狀態(tài)寄存器。它是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖?。它是const因?yàn)槌绦虿粦?yīng)該試圖去修改它。

  2) 一個(gè)指針可以是volatile 嗎?

  可以,當(dāng)一個(gè)中服務(wù)子程序修改一個(gè)指向buffer的指針時(shí)。

  3). 下面的函數(shù)有什么錯(cuò)誤:

這段代碼的目的是用來返指針ptr指向值的平方,但是,由于ptr指向一個(gè)volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:

由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

注意:頻繁地使用volatile很可能會(huì)增加代碼尺寸和降低性能,因此要合理的使用volatile。

2.1.17 有常量指針 指針常量 常量引用 沒有 引用常量

1.指針:指針代表一個(gè)變量的地址;

例如:

2.引用:引用即針對(duì)一個(gè)變量的別名,引用必須被初始化,引用作為參數(shù)(形參)時(shí),不會(huì)像指針一樣使用存儲(chǔ)單元,更不會(huì)像值傳遞一樣創(chuàng)建該參數(shù)的副本,提高空間/時(shí)間效率。

例如:int a=2,&b = a;

3.常量引用:格式為 const 變量類型 &變量名,當(dāng)聲明該引用時(shí),不可通過引用對(duì)其目標(biāo)變量的值進(jìn)行修改,變量自身可以修改,可用于保證函數(shù)內(nèi)形參不可更改,也就是保證傳入的實(shí)參為常量。

4.指向常量的指針:(《C++ Primer》書中名字是指向常量的指針,網(wǎng)上的叫法是“常量指針”),const int *p;其本質(zhì)為一個(gè)指針,因?yàn)樵撝羔樦赶蛞粋€(gè)常量,所以不能通過該指針修改常量的值,但該指針指向的也可為變量,重點(diǎn)在于不能通過該指針修改指向變量(常量)的值;

5.常量指針:(《C++ Primer》書中名字是常量指針,網(wǎng)上的叫法是“指針常量”),int* const p;其本質(zhì)為一個(gè)常量,所以其指向的值可以改變,但是由于指針為常量,所以聲明時(shí)必須初始化,且初始化后存放在指針中那個(gè)地址不可改變,此地址對(duì)應(yīng)的非常量值仍可被改變。

總結(jié): const在 * 的左邊,則為指向常量的指針,即指針指向的變量的值不可直接通過指針改變(可以通過其他途徑改變);const在 * 的右邊,則為常量指針,即指針的指向不可變。簡(jiǎn)記為const的 “左定值,右定向”。

2.1.19 c/c++中變量的作用域

對(duì)局部變量的兩點(diǎn)說明:

1)main() 也是一個(gè)函數(shù),在 main() 內(nèi)部定義的變量也是局部變量,只能在 main() 函數(shù)內(nèi)部使用。

2)形參也是局部變量,將實(shí)參傳遞給形參的過程,就是用實(shí)參給局部變量賦值的過程,它和a=b; sum=m+n;這樣的賦值沒有什么區(qū)別。

全局變量:

全局變量的默認(rèn)作用域是整個(gè)程序,也就是所有的代碼文件,包括源文件(.c文件)和頭文件(.h文件)。如果給全局變量加上 static 關(guān)鍵字,它的作用域就變成了當(dāng)前文件,在其它文件中就無效了。我們目前編寫的代碼都是在一個(gè)源文件中,所以暫時(shí)不用考慮 static 關(guān)鍵字.

————————————————

版權(quán)聲明:本文為CSDN博主「Oliver.H」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/weixin_43253519/article/details/108351553

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容