【C/C++】程序员面试,掌握这些,丹尼斯·里奇来了也难不倒你
目錄
序
嗨!這里是狐貍~~
1、const
作用
使用
2、static
作用
3、inline 內聯函數
特征
使用
優缺點
虛函數(virtual)可以是內聯函數(inline)嗎?
4、assert()
5、sizeof()
6、#pragma pack(n)
#pragma pack(n) 使用
7、位域
8、volatile
9、struct 和 typedef struct
C中
C++ 中
暫停一會
序
如果你給我的,和你給別人的是一樣的,那我就不要了。
嗨!這里是狐貍~~
又見面了,今天來補補課,來給大家帶來一些不一樣的東西,這些也是我這么多年整理出來的,一點點的積累出來的,是什么呢,就是大家以后想當程序員面試的時候需要掌握的一些些知識,可能不是很全面,但我覺得只要掌握我這些整理出來的知識,找個工作是絕對沒有問題的,東西很多哈,一時講不完,可能要分很多期了,希望大家可以耐心觀看,最好是收藏起來以備不時之需,自認為是非常詳細了。好,我們現在開始。
1、const
作用
? ? ? 1)修飾變量,說明該變量不可以被改變;
? ? ? 2)修飾指針,分為指向常量的指針和指針常量;
? ? ? 3)常量引用,經常用于形參類型,即避免了拷貝,又避免了函數對值的修改;
? ? ? 4)修飾成員函數,說明該成員函數內不能修改成員變量。
使用
??const 使用
??1)const的普通用法
int const size; const int size;這兩條語句都是把size聲明為一個整數,他的值不能被修改。
可以在聲明的時候對他進行初始化,
int const size =10;錯誤用法:
const int size =10; char buffer[size];錯誤在于,size占用某內存塊,C編譯器不知道它在編譯的值。并且,C默認const是外部連接的,所以寫成const是可以的。
2)const用于指針
(1)
const int *p; int const *p;?兩者相同,p是一個指向整形常亮的指針,可以修改指針的值,但不能修改指針所指向的值。
int b = 5; int c = 3; const int *p=&b; *p=6; //錯誤,不能通過指針修改所指向的值; p=&c; //正確,可以修改指針的值(2)
int * const p;p是一個指針,指針p是常量,它的值無法修改,但是可以修改p所指向的整型的值。
int b = 5; int c = 3; int *const p = &b; *p = 6; //正確,可以修改指針所值向的值 p =&c; //錯誤,不可以修改指針的值(3)
const int * const p;無論是指針本身還是它所指向的值都是常量。
簡單的判斷的方法:如果const在 * 的左側,則const用來修飾指針所指向的變量,即指針指向位常量;如果const位于*的右側,則const就是修飾指針本身,即指針本身就是常量。
?3)const用于函數的形參
void foo(const int * p)const用于形參時說明了形參在函數內部不會被改變。這種形式通常用于數組形式的參數中模擬傳值調用。這也是const最有用之處,限定函數的形參,這樣該函數將不會修改實參指針所指向的數據。這里需要注意得的是,是函數不應該去修改而不是不能修改。
4)聲明常量的其他方法
C語言中可以用enum類型和#define宏來定義常量
#define MAX_SIZE 10; int const max_size =10;此處使用#define比使用const變量更好。
只要允許使用字面值常量的地方都可以使用前者,比如聲明數組的長度。
const變量只能用于允許使用變量的地方。
2、static
作用
修飾普通變量,修改變量的存儲區域和生命周期,使變量存儲在靜態區,在 main 函數運行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統用默認值初始化它。
修飾普通函數,表明函數的作用范圍,僅在定義該函數的文件內才能使用。在多人開發項目時,為了防止與他人命令函數重名,可以將函數定位為 static。
修飾成員變量,修飾成員變量使所有的對象只保存一個該變量,而且不需要生成對象就可以訪問該成員。
修飾成員函數,修飾成員函數使得不需要生成對象就可以訪問該函數,但是在 static 函數內不能訪問非靜態成員。
靜態局部變量定義:
????在局部變量的說明前再加上static說明符就構成靜態局部變量。
例如:
static int a,b;static float array[5]={1,2,3,4,5};3、inline 內聯函數
特征
-
相當于把內聯函數里面的內容寫在調用內聯函數處;
-
相當于不用執行進入函數的步驟,直接執行函數體;
-
相當于宏,卻比宏多了類型檢查,真正具有函數特性;
-
不能包含循環、遞歸、switch 等復雜操作;
-
在類聲明中定義的函數,除了虛函數的其他函數都會自動隱式地當成內聯函數。
使用
inline 使用
// 聲明1(加 inline,建議使用) inline int functionName(int first, int secend,...);// 聲明2(不加 inline) int functionName(int first, int secend,...);// 定義 inline int functionName(int first, int secend,...) {/****/};// 類內定義,隱式內聯 class A {int doA() { return 0; } // 隱式內聯 }// 類外定義,需要顯式內聯 class A {int doA(); } inline int A::doA() { return 0; } // 需要顯式內聯編譯器對 inline 函數的處理步驟
將 inline 函數體復制到 inline 函數調用點處;
為所用 inline 函數中的局部變量分配內存空間;
將 inline 函數的的輸入參數和返回值映射到調用方法的局部變量空間中;
如果 inline 函數有多個返回點,將其轉變為 inline 函數代碼塊末尾的分支(使用 GOTO)。
優缺點
優點
內聯函數同宏函數一樣將在被調用處進行代碼展開,省去了參數壓棧、棧幀開辟與回收,結果返回等,從而提高程序運行速度。
內聯函數相比宏函數來說,在代碼展開時,會做安全檢查或自動類型轉換(同普通函數),而宏定義則不會。
在類中聲明同時定義的成員函數,自動轉化為內聯函數,因此內聯函數可以訪問類的成員變量,宏定義則不能。
內聯函數在運行時可調試,而宏定義不可以。
缺點
代碼膨脹。內聯是以代碼膨脹(復制)為代價,消除函數調用帶來的開銷。如果執行函數體內代碼的時間,相比于函數調用的開銷較大,那么效率的收獲會很少。另一方面,每一處內聯函數的調用都要復制代碼,將使程序的總代碼量增大,消耗更多的內存空間。
inline 函數無法隨著函數庫升級而升級。inline函數的改變需要重新編譯,不像 non-inline 可以直接鏈接。
是否內聯,程序員不可控。內聯函數只是對編譯器的建議,是否對函數內聯,決定權在于編譯器。
虛函數(virtual)可以是內聯函數(inline)嗎?
-
虛函數可以是內聯函數,內聯是可以修飾虛函數的,但是當虛函數表現多態性的時候不能內聯。
-
內聯是在編譯器建議編譯器內聯,而虛函數的多態性在運行期,編譯器無法知道運行期調用哪個代碼,因此虛函數表現為多態性時(運行期)不可以內聯。
-
inline virtual 唯一可以內聯的時候是:編譯器知道所調用的對象是哪個類(如 Base::who() ),這只有在編譯器具有實際對象而不是對象的指針或引用時才會發生。
虛函數內聯使用
#include <iostream> using namespace std; class Base { public:inline virtual void who(){cout << "I am Base\n";}virtual ~Base() {} }; class Derived : public Base { public:inline void who() // 不寫inline時隱式內聯{cout << "I am Derived\n";} };int main() {// 此處的虛函數 who(),是通過類(Base)的具體對象(b)來調用的,編譯期間就能確定了,所以它可以是內聯的,但最終是否內聯取決于編譯器。 Base b;b.who();// 此處的虛函數是通過指針調用的,呈現多態性,需要在運行時期間才能確定,所以不能為內聯。 Base *ptr = new Derived();ptr->who();// 因為Base有虛析構函數(virtual ~Base() {}),所以 delete 時,會先調用派生類(Derived)析構函數,再調用基類(Base)析構函數,防止內存泄漏。delete ptr;ptr = nullptr;system("pause");return 0; }4、assert()
斷言,是宏,而非函數。assert 宏的原型定義在 <assert.h> (C)、 <cassert> (C++)中,其作用是如果它的條件返回錯誤,則終止程序執行。如:
assert( p != NULL );5、sizeof()
-
sizeof 對數組,得到整個數組所占空間大小。
-
sizeof 對指針,得到指針本身所占空間大小。
6、#pragma pack(n)
設定結構體、聯合以及類成員變量以 n 字節方式對齊
#pragma pack(n) 使用
#pragma pack(push) // 保存對齊狀態 #pragma pack(4) // 設定為 4 字節對齊struct test {char m1;double m4;int m3; };#pragma pack(pop) // 恢復對齊狀態7、位域
Bit mode: 2; // mode 占 2 位類可以將其(非靜態)數據成員定義為位域(bit-field),在一個位域中含有一定數量的二進制位。當一個程序需要向其他程序或硬件設備傳遞二進制數據時,通常會用到位域。
-
位域在內存中的布局是與機器有關的
-
位域的類型必須是整型或枚舉類型,帶符號類型中的位域的行為將因具體實現而定
-
取地址運算符(&)不能作用于位域,任何指針都無法指向類的位域
8、volatile
volatile int i = 10;-
volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統、硬件、其它線程等)更改。所以使用 volatile 告訴編譯器不應對這樣的對象進行優化。
-
volatile 關鍵字聲明的變量,每次訪問時都必須從內存中取出值(沒有被 volatile 修飾的變量,可能由于編譯器的優化,從 CPU 寄存器中取值)
-
const 可以是 volatile (如只讀的狀態寄存器)
-
指針可以是 volatile
9、struct 和 typedef struct
C中
// c typedef struct Student {int age; } S;等價于
// c struct Student { int age; };typedef struct Student S;此時?S?等價于?struct Student?,但兩個標識符名稱空間不相同。
另外還可以定義與?struct Student?不沖突的?void Student() {}?。
C++ 中
由于編譯器定位符號的規則(搜索規則)改變,導致不同于C語言。
1)如果在類標識符空間定義了?struct Student {...};?,使用?Student me;?時,編譯器將搜索全局標識符表,?Student?未找到,則在類標識符內搜索。
即表現為可以使用?Student?也可以使用?struct Student?,如下:
// cpp struct Student { int age; };void f( Student me ); // 正確,"struct" 關鍵字可省略2)若定義了與Student同名函數之后,則Student只代表函數,不代表結構體,如下:?
typedef struct Student { int age; } S;void Student() {} // 正確,定義后 "Student" 只代表此函數//void S() {} // 錯誤,符號 "S" 已經被定義為一個 "struct Student" 的別名int main() {Student(); struct Student me; // 或者 "S me";return 0; }暫停一會
OK,大家能看到這里估計都很累了吧,我也越寫越覺得這種方式是不是你們接受不了,確實有些枯燥了,而且又長又臭,大家覺得呢,之后看能不能一個知識點的講,看你們想看什么,確實一下寫太多,太長,有些確實可能也沒必要,好叭,看以后怎么個改法,今天就到這里啦,在這里希望可以得到大家的關注以及點贊啦,希望大家可以多多支持!!你們的點贊就是我堅持下去的動力!
后續我還會發布更多的項目源或者學習資料,希望大家可以持續關注,有什么問題可以回帖留言。想要提前掌握的可以加群領取C/C++學習資料以及其他項目的源碼的可以加群【1083227756】了解。想要對程序員的未來發展有興趣的可以關注微信公眾號:【狐貍的編碼時光】,希望和大家一起學習進步
http://點擊鏈接加入群聊【C語言/C++編程學習交流】:https://jq.qq.com/?_wv=1027&k=Jq9eZFF5http://點擊鏈接加入群聊【C語言/C++編程學習交流】:https://jq.qq.com/?_wv=1027&k=Jq9eZFF5
總結
以上是生活随笔為你收集整理的【C/C++】程序员面试,掌握这些,丹尼斯·里奇来了也难不倒你的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch 基本数学运算
- 下一篇: 【Semantic Embedding】