c++實際應用匯總(七)計算機與編譯原理 | 內聯與虛函數 | sizeof

目錄

一、CPU的總線

1.1 32位與64位

1.2 總線類型

1.3 32與64下的內存

二、static與const

2.1 什麼是static?

2.2 const

2.3 指針與const的關係

四、虛函數|類|父類

4.1 內聯

4.2 虛函數

4.3 虛函|內聯

4.4 靜態函數

4.5 幾種存儲方式

五、sizeof(int)在什麼過程中出結果

5.1 sizeof數據類型

5.2 sizeof 結構體

5.3 sizeof union

5.4 sizeof數組

5.5 sizeof指針

5.6 sizeof 函數


一、CPU的總線

1.1 32位與64位

32位CPU與64位CPU指的是什麼?

指的數據總線。

https://www.cnblogs.com/wtch519361/p/5278343.html

64位數據總線一次就能取出64bit的數據,8位數據總線的CPU一次只能取出8bit的數據

CPU總線,是PC系統中最快的總線,也是芯片組與主板的核心。這條總線主要由CPU使用,用來與高速緩存、主存和北橋(或MCH)之間傳送信息。

來自 <https://baike.baidu.com/item/CPU%E6%80%BB%E7%BA%BF/15739624?fr=aladdin>

1.2 總線類型

數據總線DB(Data Bus)、地址總線AB(Address Bus)和控制總線CB(Control Bus),也統稱爲系統總線,即通常意義上所說的總線。

  • 數據總線(Data Bus):在CPU與RAM之間來回傳送需要處理或是需要儲存的數據。
  • 地址總線(Address Bus):用來指定在RAM(Random Access Memory)之中儲存的數據的地址。
  • 控制總線(Control Bus):將微處理器控制單元(Control Unit)的信號,傳送到周邊設備。
  • 擴展總線(Expansion Bus):外部設備和計算機主機進行數據通信的總線,例如ISA總線,PCI總線。
  • 局部總線(Local Bus):取代更高速數據傳輸的擴展總線。

來自 <https://baike.baidu.com/item/%E6%80%BB%E7%BA%BF/108823>

1.3 32與64下的內存

除了long和void之外

Int,double,float的內存佔用沒有變化

具體內容:

http://www.unix.org/whitepapers/64bit.html

二、static與const

2.1 什麼是static?

靜態變量(Static Variable)在計算機編程領域指在程序執行前系統就爲之靜態分配(也即在運行時中不再改變分配情況)存儲空間的一類變量。

存儲類名

生命週期

作用域

extern

靜態(程序結束後釋放)

外部(整個程序)

static

靜態(程序結束後釋放)

內部(僅翻譯單元,一般指單個源文件)

auto,register

函數調用(調用結束後釋放)

定義全局變量前,加上關鍵字static,該變量就被定義成了一個靜態全局變量.

特點:

  • 該變量在全局數據區分配內存.
  • 初始化:如果不是顯示初始化,那麼將被隱式初始化爲0.
  • 訪變量只在本文件可見,即應該爲定義之處開始到本文件結束.

程序在內存中一般分爲四個區域:

  • 代碼區
  • 全局數據區
  • 堆區
  • 棧區

一般程序由new產生的動態數據放在堆區,函數內部的自動變量放在棧區.自動變量一般會隨着函數的退出而釋放空間,靜態數據(即使是函數內部的靜態局部變量)都存放在全局數據區.因此它們並不會隨着函數的退出而釋放空間.

 static int n;//定義靜態全局變量

改爲:  int n;//定義全局變量

相關問題與理解

在C語言中,關於靜態變量的說法,正確的是( )。

  • 靜態變量和常量的作用相同(錯誤,靜態變量表示內存區中固定位置的變量)
  • 函數中的靜態變量,在函數退出後不被釋放(正確)
  • 靜態變量只可以賦值一次,賦值後則不能改變(錯誤,靜態變量只是內存區中的位置不能改變)
  • 靜態全局變量的作用域爲一個程序的所有源文件(錯誤,只是當前程序文件,想要擴展帶所有需要加extern修飾)

解析:

正確答案選第二個,靜態變量在函數退出後不會被釋放。因爲靜態變量是程序執行之前系統就靜態分配的變量了。

作用域爲當前文件,從定義/聲明位置到文件結尾。動態全局變量可以通過extern關鍵字在外部文件中使用,但靜態全局變量不可以在外部文件中使用。靜態全局變量相當於限制了動態全局變量的作用域。

靜態變量並不是說其就不能改變值,不能改變值的量叫常量。 其擁有的值是可變的 ,而且它會保持最新的值。說其靜態,是因爲它不會隨着函數的調用和退出而發生變化。即上次調用函數的時候,如果我們給靜態變量賦予某個值的話,下次函數調用時,這個值保持不變。

static在類內初始化還是類外初始化?

在C++中,類的靜態成員(static member)必須在類內聲明,在類外初始化,像下面這樣。

class A
{
private:
	static int count; // 類內聲明
};

爲什麼?因爲靜態成員屬於整個類,而不屬於某個對象,如果在類內初始化,會導致每個對象都包含該靜態成員,這是矛盾的。

2.2 const

const是一個C語言(ANSI C)的關鍵字,具有着舉足輕重的地位。它限定一個變量不允許被改變,產生靜態作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在觀看別人代碼的時候,清晰理解const所起的作用,對理解對方的程序也有一定幫助。

加了const修飾必須在一開始就初始化值,下面代碼編譯時候就會報錯,因爲沒有定義時就初始化值;

const int a;
a = 10;

下面代碼編譯的時候就會報錯,因爲const的值被更改了。
 

const int a=10;
cin >> a;

2.3 指針與const的關係

1)指針指向的變量的值不能變,指向可變

int x = 1;
int y = 2;
const int* px = &x;
int const* px = &x; //這兩句表達式一樣效果
px = &y; //正確,允許改變指向
*px = 3; //錯誤,不允許改變指針指向的變量的值

2)指針指向的變量的值可以改變,指向不可變

int x = 1;
int y = 2;
int* const px = &x;
px = &y; //錯誤,不允許改變指針指向
*px = 3; //正確,允許改變指針指向的變量的值

3)指針指向的變量的值不可變,指向不可變

int x = 1;
int y = 2;
const int* const px = &x;
int const* const px = &x;
px = &y; //錯誤,不允許改變指針指向
*px = 3; //錯誤,不允許改變指針指向的變量的值

四、虛函數|類|父類

 以下描述正確的是( )?

  • 虛函數是可以內聯的,可以減少函數調用的開銷,提高效率(錯誤,指針調用的時候,類不確定,所以虛函數不內聯)
  • 類裏面可以同時存在函數名和參數都一樣的虛函數和靜態函數(靜態函數文件之外的函數可以與靜態函數重名,但是文件只能的函數不能與靜態函數重名,調用時候無法調用,存在二義性)
  • 父類的析構函數是非虛的,但是子類的析構函數是虛的,delete子類對象指針會調用父類的析構函數
  • 選項都不對

解析:正確選項爲C,刪除子類後,會調用子類的析構函數,子類的析構函數是虛函數,因此往上尋找父類,調用了父類的析構函數。

4.1 內聯

計算機科學中,內聯函數(有時稱作在線函數編譯時期展開函數)是一種編程語言結構,用來建議編譯器對一些特殊函數進行內聯擴展(有時稱作在線擴展)。

inline?Inline function。即可以理解爲編譯器直接講函數編譯到程序之中,而不是去調用。

4.2 虛函數

https://baike.baidu.com/item/%E8%99%9A%E5%87%BD%E6%95%B0/2912832?fr=aladdin

通過基類中virtual修飾的函數。在派生類中重新定義的成員函數。

用法格式爲:virtual 函數返回類型 函數名(參數表) {函數體};實現多態性,通過指向派生類的基類指針或引用,訪問派生類中同名覆蓋成員函數。

虛函數具有多態性。以不同的策略實現不同的方法。

簡單地說,那些被virtual關鍵字修飾的成員函數,就是虛函數。虛函數的作用,用專業術語來解釋就是實現多態性(Polymorphism),多態性是將接口與實現進行分離;用形象的語言來解釋就是實現以共同的方法,但因個體差異,而採用不同的策略。

虛函數實例
 

class A
{
public:
	void print()
	{
		cout << "This is A" << endl;
	}
};

class B : public A
{
public:
	void print()
	{
		cout << "This is B" << endl;
	}
};

int main()
{
	A a;
	B b;
	A *ptr_a = &a;
	B *ptr_b = &b;
	a.print();
	b.print();
	ptr_a->print();
	ptr_b->print();
	int end; cin >> end;
	return 0;
}

輸出:

This is A
This is B
This is A
This is B

如果將main函數改爲下面這樣:

int main()
{
	//main2
	A a;
	B b;
	A *p1 = &a;
	A *p2 = &b;
	p1->print();
	p2->print();
	a.print();
	b.print();
	int end; cin >> end;
	return 0;
}

輸出就爲

This is A
This is A
This is A
This is B

爲什麼會這樣?直接用類就會調用當前類內的函數,用指針就調用父類的函數。

使用類的引用或指針調用虛函數時,無論虛函數什麼時間被調用,它都不能是內聯的(因爲虛函數的調用在運行時確定,這樣就會調用父類);

但是,使用類的對象調用虛函數時,無論虛函數什麼時間被調用,它都可以是內聯的(因爲在編譯時編譯器知道對象的確定類型)

爲了避免這個問題,所以就實現了虛函數,在每次繼承中都需要重新初始化。

class A
{
    public:
        virtual void print(){cout<<"This is A"<<endl;}
};

class B : public A
{
    public:
    void print(){cout<<"This is B"<<endl;}
};

這樣無論如何都能輸出:

This is A
This is B
This is A
This is B

解析:基類中函數定義爲虛函數,則派生類中函數自動定義爲虛函數,每次派生類中都需要重新定義。

4.3 虛函|內聯

https://blog.csdn.net/shuangshuang37278752/article/details/38875351

虛函數用於運行時多態或晚綁定或動態綁定,內聯函數用於提高程序的效率,內聯函數的思想:無論內聯函數什麼時間被調用,在編譯時,內聯函數的代碼段都可以在被替換或插入,當一些小函數在程序中被頻繁使用和調用時,內聯函數變得十分有用。

類內部定義的所有函數(虛函數除外)都被隱式或自動認爲是內聯的

使用類的引用或指針調用虛函數時,無論虛函數什麼時間被調用,它都不能是內聯的(因爲虛函數的調用在運行時確定);

但是,使用類的對象調用虛函數時,無論虛函數什麼時間被調用,它都可以是內聯的(因爲在編譯時編譯器知道對象的確定類型)

所以,虛函數不同條件下是否內聯不確定,但是是可以內聯的。類內調用可以內聯。

4.4 靜態函數

https://baike.baidu.com/item/%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0/5644260

全局函數:在類外聲明static函數的話,相當於一個全局函數,由於由於 static 的限制,它只能在文件所在的編譯單位內使用,不能在其它編譯單位內使用。

靜態函數:需要出現在類中,函數調用的結果不會訪問或者修改任何對象(非static)數據成員,這樣的成員聲明爲靜態成員函數比較好。它爲該類的公用變量,在第一次使用時被初始化,對於該類的所有對象來說,static成員變量只有一份。

靜態函數特點:

<1> 其他文件中可以定義相同名字的函數,不會發生衝突

<2> 靜態函數不能被其他文件所用。

內部函數又稱靜態函數。但此處“static”的含義不是指存儲方式,而是指對函數的作用域僅侷限於本文件。 使用內部函數的好處是:不同的人編寫不同的函數時,不用擔心自己定義的函數,是否會與其它文件中的函數同名,因爲同名也沒有關係。

4.5 幾種存儲方式

存儲說明符auto,register,extern,static,對應兩種存儲期:

自動存儲期和靜態存儲期。 auto和register對應自動存儲期。具有自動存儲期的變量在進入聲明該變量的程序塊時被建立,它在該程序塊活動時存在,退出該程序塊時撤銷。

關鍵字extern和static用來說明具有靜態存儲期的變量和函數。用static聲明的局部變量具有靜態存儲持續期(static storage duration),或靜態範圍(static extent)。雖然他的值在函數調用之間保持有效,但是其名字的可視性仍限制在其局部域內。靜態局部對象在程序執行到該對象的聲明處時被首次初始化。

五、sizeof(int)在什麼過程中出結果

編譯的階段,編譯,鏈接,運行?

它在編譯時候的運算符號.在Pascal 語言與C語言中,對 sizeof() 的處理都是在編譯階段進行。

解析:

https://www.cnblogs.com/huolong-blog/p/7587711.html

5.1 sizeof數據類型

short、int、long、float、double,不同操作平臺上結果不一樣

5.2 sizeof 結構體

結構體內需要字節對齊,以加快計算機的取數速度.所以,結構體以填充字節的形式來實現字節對齊.保證讓寬度爲2的基本數據類型(short等)都位於能被2整除的地址上,讓寬度爲4的基本數據類型(int等)都位於能被4整除的地址上.  空結構體(不含數據成員)的sizeof值爲1

1)  結構體變量的首地址能夠被其最寬基本類型成員的大小所整除。

2)  結構體的每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要,編譯器會在成員之間加上填充字節(internal adding)。

3)  結構體的總大小爲結構體最寬基本類型成員大小的整數倍,如有需要,編譯器會在最末一個成員後加上填充字節(trailing padding)。
 

struct S1
{
	char a;
	int b;
};
sizeof(S1); //值爲8,字節對齊,在char之後會填充3個字節。 

struct S2
{
	int b;
	char a;
};
sizeof(S2); //值爲8,字節對齊,在char之後會填充3個字節。 

struct S3
{
};
sizeof(S3); //值爲1,空結構體也佔內存

5.3 sizeof union

是union中的內存的最大值。共用體和結構體都是由多個不同的數據類型成員組成, 但在任何同一時刻, 共用體只存放了一個被選中的成員。

例如:

union u
{
	int a;
	float b;
	double c;
	char d;
};

5.4 sizeof數組

返回數組的大小。

如果數組是char a[10],這種,sizeof返回10

但是如果是 char a[]="123",需要把結尾的\0 考慮上,返回值是4

對於函數的形參,則返回指針的大小。例如:

void func(char a[3])
{
	int c = sizeof(a); //c = 4,因爲這裏a不在是數組類型,而是指針,相當於char *a。 
}

void funcN(char b[])
{
	int cN = sizeof(b); //cN = 4,理由同上。 
}
sizeof(u); //值爲8

5.5 sizeof指針

指針爲地址總線的寬度。比如32位機中爲4, 64位機中,還是4嗎?

	char *b = "helloworld";
	char *c[10];
	double *d;
	int **e;
	void(*pf)();

	cout << "char *b = /"helloworld / "     " << sizeof(b) << endl;//指針指向字符串,值爲4 
	cout << "char *b                    " << sizeof(*b) << endl; //指針指向字符,值爲1 
	cout << "double *d                  " << sizeof(d) << endl;//指針,值爲4 
	cout << "double *d                  " << sizeof(*d) << endl;//指針指向浮點數,值爲8 
	cout << "int **e                  " << sizeof(e) << endl;//指針指向指針,值爲4 
	cout << "char *c[10]                " << sizeof(c) << endl;//指針數組,值爲40 
	cout << "void (*pf)();              " << sizeof(pf) << endl;//函數指針,值爲4

5.6 sizeof 函數

相當於求函數的返回值,不能求void函數的值。且函數不會被調用。

float FuncP(int a, float b)
{
	return a + b;
}

int FuncNP()
{
	return 3;
}

void Func()
{
	return;
}

int main()
{
	cout << sizeof(FuncP(3, 0.4)) << endl; //OK,值爲4,sizeof(FuncP(3,0.4))相當於sizeof(float) 
	cout << sizeof(FuncNP()) << endl; //OK,值爲4,sizeof(FuncNP())相當於sizeof(int) 
	/*cout<<sizeof(Func())<<endl; //error,sizeof不能對返回值爲空類型的函數求值*/
	/*cout<<sizeof(FuncNP)<<endl; //error,sizeof不能對函數名求值*/
	int end; cin >> end;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章