?? c++5.dat
字號:
?
+
值傳遞也可以稱之為"賦值調用",這種方式是把實參的值復制到函數的形式參數中,函數中的形式參數的任何變化都不會影響到實參變量的值.
3.3 函數調用過程
C++函數調用是基于棧存儲結構來實現的.當函數被調用時,為函數形參、返回值和其它變量等在棧中分配內存空間,當函數返回時,這些內存空間又被釋放,以便于再利用.
第四節 inline(內聯函數)
我們看下面的函數,函數體中只有一行語句:
double Average(double total, int number)
{
return total/number;
}
定義這么簡單的函數有必要嗎?實際上,它還是有一些優點的:第一,它使程序更可讀;第二,它使這段代碼可以重復使用.但是,它也有缺點:當它被頻繁地調用的時候,由于調用函數的開銷,會對應用程序的性能有損失.例如,Average在一個循環語句中重復調用幾千次,會降低程序的執行效率.
那么,有辦法避免函數調用的開銷嗎?對于上面的函數,我么可以把它定義為內聯函數的形式:
inline double Average(double total, int number)
{
return total/number;
上面的內聯函數同它的非內聯函數相比,僅僅是多了一個關鍵字inline,它們在功能上并沒有區別:前者也是有兩個形參,一個double型,一個int型,返回值是double型,且兩個形參相除后所得的商作為結果返回.但是,編譯器對這兩個函數的調用過程的處理是不同的.對于內聯函數的調用,編譯器是將其函數體放在調用的地方,沒有非內聯函數調用時的棧內存的創建和釋放開銷.但是,所執行的計算是完全相同的.
使用內聯函數時應注意以下幾個問題:
(1) 在一個文件中定義的內聯函數不能在另一個文件中使用.它們通常放在頭文件中共享.
(2) 內聯函數應該簡潔,只有幾個語句,如果語句較多,不適合于定義為內聯函數.
(3) 內聯函數體中,不能有循環語句、if語句或switch語句,否則,函數定義時即使有inline關鍵字,編譯器也會把該函數作為非內聯函數處理.
(4) 內聯函數要在函數被調用之前聲明.例如下面的代碼將內聯函數放在函數調用之后聲明,不能起到預期的效果.
第五節 變量的作用域與存儲期
在C++中,變量有效的范圍(稱為變量的作用域)和被存儲的時間(稱為變量的存儲期或生存期)都是不同的.如果按變量的作用域來分類的話,變量可以分為局部變量和全局變量;如果按變量的存儲期來分類的話,變量可以分為外部變量、靜態變量、自動變量、寄存器變量.下面對各種類型的變量分別進行介紹.
5.1 作用域的概念
在C++中變量、函數和類(我們在后面要介紹類)的作用域是不同的,在函數和類外定義的變量,具有全局的作用域,這些變量稱之為全局變量.同樣,在類外定義的函數,我們稱之為全局函數.
把一些C++語句,我們由一對花括號括起來,稱之為語句塊或塊,在塊中定義的變量作用域在塊內,稱之為局部作用域,在局部作用域中定義的變量稱之為局部變量.例如,在函數和在一個復合語句中定義的變量都是局部變量.函數的形參的作用域在函數內,也是局部變量.局部變量只在局部作用域內有效,我們稱之為可見,離開了其所在的局部作用域便無效,或稱之為不可見.
在同一個作用域內,變量不能同名,否則,程序編譯時,編譯器會給出變量重復定義的錯誤.不同的作用域內,變量同名不會出現語法問題,但可能會使某些變量不能訪問.例如:
int xyz; // xyz全局變量
void Foo (int xyz) // xyz 是 Foo函數中的局部變量
{
if (xyz > 0) {
double xyz; // xyz是if語句塊中的局部變量
//...
}
}
上面的程序段中有三個作用域,全局作用域及兩個局部作用域,且if語句塊作用域嵌套在Foo函數作用域內.在if語句塊內,訪問不到Foo函數中的xyz變量,這說明內部作用域會覆蓋外部作用域.需要注意的是:運用全局運算符::,我們可以在局部作用域中訪問到全局變量.例如,在上面的if語句塊中,我們增加一條語句:
double t = ::xyz;
就可以把全局變量xyz的值賦給局部變量t.
5.2 局部變量和全局變量
我們已經知道了什么叫全局變量,什么叫局部變量.一個應用程序可能包含多個源文件,而一個源文件可能包含多個函數.一般說來,全局變量的作用范圍是定義點起至文件結束為止,局部變量的作用范圍是從定義點起至該局部變量所在塊的尾部為止.變量的存儲期也限制在其作用域內.例如:全局變量的存儲期與應用程序的生存期相同,局部變量是在進入作用域時創建,而在退出作用域時被銷毀.
一、局部變量
由于局部變量的存儲期是是在其所在的局部作用域內,其內存也是由系統自動分配的,所以,它也被稱為自動變量.可以用一個關鍵字auto顯式指定一個變量是自動變量.例如:
void Foo (void)
{
auto int xyz; // 等價于int xyz;
//...
}
一般說來,我們很少使用auto關鍵字.因為C++中,局部變量默認為自動變量.
我們已經知道,變量是存放在內存中.如果程序對一些頻繁使用的變量(如循環變量),要求更高的訪問效率,可以把這些變量保存在寄存器中,保存在寄存器中的變量,我們也稱之為寄存器變量.
寄存器變量也是局部變量,定義寄存器變量需要用register關鍵字,例如:
for (register int i = 0; i < n; ++i)
sum += i;
需要注意的是:有時我們用register關鍵字定義了寄存器變量,編譯器也可能不把該變量放在寄存器中,而放在內存中.因為機器的寄存器個數是有限,當我們申請寄器存放變量時,可能所有的寄存器都在被使用中.
二、全局變量
我們知道:全局變量是在函數和類外定義的,所以也稱之為外部變量.全局變量一旦定義,從定義點開始至文件結束的所有函數都可以使用該變量.
一般情況下,我們把全局變量的定義放在引用它的所有函數之前.但是,如果在全局變量定義點之前的函數要引用該全局變量或另一個源文件中的函數要引用該全局變量,需要在函數內對要引用的全局變量加extern說明.例如:
#include <stdio.h>
int max(int x, int y);
main()
{
extern int a, b; //全局變量說明,而非定義
printf("%d\n", max(a, b));
return 0;
}
int a = 13, b = -8;
int max(int x, int y)
{
int z;
z = x > y ? x : y;
return z;
}
注意:用extern說明全局變量的時候,不能給初值.例如:
extern int size = 10; // 不再是說明!
因為這會使得size變成變量的定義,而不是說明,編譯器會為它分配內存.如果別的地方定義了全局變量size,該程序在編譯時,編譯器會給出變量重復定義的錯誤.
使用全局變量,在我們編程中,有時會來一些方便.但是,它也有許多副作用:在程序的整個執行過程中始終占用內存空間,使程序的可讀性、通用性和可移植性降低等,建議不在必要時,不使用全局變量
5.3 靜態變量
如果在變量的定義前加上static關鍵字,就定義了靜態變量.例如:
static int shortestRoute; // 靜態全局變量
void Error (char *message)
{
static int count = 0; // 靜態局部變量
...
}
在上面的程序段中,我們定義了一個靜態全局變量shortestRoute和一個靜態局部變量count.靜態變量與全局變量具有相同的存儲期,它們均與應用程序的生存期相同.但是,靜態的全局變量只能在定義該全局變量的文件中訪問,靜態的局部變量只能在定義該局部變量的局部作用域中訪問.
靜態變量這種特性是有用的,如果我們需要某些全局變量只在本文件中訪問,就可以把它們定義為靜態的,這也可以減少了不同文件中定義的同名的全局變量而發生沖突的可能性,從而提高了程序的可移植性.
static關鍵字不僅可以放在變量的定義前,也可以放在函數的定義前.在全局函數的定義前加上了static關鍵字,就稱為靜態全局函數.例如:
static int FindNextRoute (void) // 僅在本文件中訪問
{
//...
}
與靜態全局變量的特性相似,靜態全局函數也只能在定義該全局函數的文件中訪問.
靜態局部變量的特性也是很有用的.例如,假定我們在一個函數中定義了一個局部變量,需要該局部變量在函數退出時并不釋放,下一次進入該函數時,局部變量原來的值還存在,我們就可以把該局部變量定義為靜態的.
第七節 函數的重載
C++中,當有一組函數完成相似功能時,函數名允許重復使用,編譯器根據參數表中參數的個數或類型(不能根據形參變量名)來判斷調用哪一個函數,這就是函數的重載.
重載函數只要其參數表中參數個數或類型不同,就視為不同的函數.例如:
#include <stdio.h>
void show(int val)
{
printf("Integer: %d\n", val);
}
void show(double val)
{
printf("Double: %lf\n", val);
}
void show(char *val)
{
printf("String: %s\n", val);
}
int main()
{
show(12);
show(3.1415);
show("Hello World\n!");
return (0);
上面的程序段定義了三個有相同名字的函數show,但形參不同,分別為int、double和char *類型(這是指向字符的指針類型,有關指針類型,在第七章作詳細介紹).
定義重載的函數時, 我們應該注意以下幾個問題:
(1) 避免函數名字相同,但功能完全不同的情形.例如上面的重載函數show的功能就是相關的,它們均是向屏幕打印信息.
(2) 函數的形參變量名不同不能作為函數重載的依據.
(3) C++中不允許幾個函數名相同、形參個數和類型也相同,僅僅是返回值不同的情形,否則,程序編譯時會出現函數重復定義的錯誤.這是因為我們編程時常常忽略返回值,例如下面的代碼段:
printf("Hello World!\n");
就沒有函數printf函數返回值的信息(順便提及:printf函數返回值是一個整數,表示打印出的字符的個數.這個返回值,我們實際上從來不用.).如果兩個重載的printf函數僅僅是返回值不同,編譯器便不能區分它們.
(4) 函數重載有時可能會產生意想不到的結果.例如:
show(0);
在上面給定的三個函數中,0可以被解釋為一個空指針NULL,即(char *)0,也可以解釋為一個整數0.C++選擇調用有整型參數的show函數,這也可能不是你所期望的結果.
(5) 調用重載的函數時,如果實參類型與形參類型不匹配,編譯器會自動進行類型轉換.如果轉換后仍然不能匹配到重載的函數,則會產生一個編譯錯誤.例如:
#include <stdio.h>
void add(int val)
{
printf("Int: %d\n", val)
}
void add(long val)
{
printf("Int: %ld\n", val)
}
void main()
{
add(3.2); //調用發生歧異
}
編譯器會由于無法決定將3.2轉換成int還是long而產生調用發生歧異錯誤.
所謂函數重載是指同一個函數名可以對應著多個函數的實現.例如,可以給函數名add()定義多個函數實現,該函數的功能是求和,即求兩個操作數的和.其中,一個函數實現是求兩個int型數之和,另一個實現是求兩個浮點型數之和.每種實現對應著一個函數體,這些函數的名字相同,但是函數參數的個數或類型不同,這就是函數重載的概念.
當調用多個同名的重載函數時,要求能夠唯一地確定應執行哪一個函數,這是通過函數參數的個數和類型來區分的.所以,重載的函數要求參數個數或者參數類型上不同,否則會出現編譯錯誤.下面是一個函數重載的例子,"int add(int i,int j)"函數實現整數的加法,"double add(double i,double j)"函數實現雙精度浮點數的加法,它們的名字相同,僅僅是參數的類型不同,min函數的功能是在若干個整數中,求最小的整數,"int min(int a,int b)"、"int min(int a,int b,int c)"和"int min(int a,int b,int c,int d)"參數類型相同,僅僅是參數個數不同.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -