C 函式的引數傳遞

2021-07-15 23:51:20 字數 4650 閱讀 1138

所有的函式都使用 在程式執行棧中分配的儲存區。該儲存區一直保持與該函式相關聯,直到函式結束為止。那時,儲存區將自動釋放以便重新使用。該函式的整個儲存區稱為活動記錄。

系統在函式的活動記錄中為函式的每個引數都提供了儲存區, 引數的儲存長度由它的型別來決定。 引數傳遞是指用函式呼叫的實參值來初始化函式引數儲存區的過程 。

c++中引數傳遞的預設初始化方法是把實參的值拷貝到引數的儲存區中 這被稱為按值傳遞 (pass-by-value )。

按值傳遞時 ,函式不會訪問當前呼叫的實參。 函式處理的值是它本地的拷貝, 這些拷貝被儲存在執行棧中, 因此改變這些值不會影響實參的值 。一旦函式結束了, 函式的活動記錄將從棧中彈出 ,這些區域性值也就消失了 。

在按值傳遞的情況下 ,實參的內容沒有被改變。 這意味著程式設計師在函式呼叫時無需儲存和恢復實參的值 。如果沒有按值傳遞機制, 那麼每個沒有被宣告為 const 的引數就可能會隨 每次函式呼叫而被改變 。按值傳遞的危害最小, 需要使用者做的工作也最少 。毫無疑問 ,按值 傳遞是引數傳遞合理的預設機制。

但是 ,按值傳遞並不是在所有的情況下都適合 。不適合的情況包括  :

當大型的類物件必須作為引數傳遞時, 對實際的應用程式而言 ,分配物件並拷貝到 棧中的時間和空間開銷往往過大。  

當實參的值必須被修改時。 例如, 在函式 swap()中, 使用者想改變實參的值 ,但是在 按值傳遞的情況下無法做到 。

// swap() 沒有交換兩個實參的值!

void swap( int v1, int v2 )

swap()交換實參的本地拷貝 ,代表 swap()實參的變數並沒有被改變 ,這將在下面呼叫 swap()的程式中可以看出來:

#include

void swap( int, int );

int main()  

編譯並執行程式產生如下結果 

before swap():  i: 10  j: 20

after swap():  i: 10  j: 20 

為了獲得期望的行為, 程式設計師可以使用兩種方法, 一種方法是, 引數被宣告成指標, 例 如 swap()可重寫如下:

// pswap()交換 v1 和 v2 指向的值

void pswap( int *v1, int *v2 )

我們必須修改 main()來呼叫 pswap() ,現在程式設計師必須傳遞兩個物件的位址而不是物件本身 。

pswap( &i, &j ); 

修改後的程式編譯執行後的結果顯示了它的正確性 

// 使用指標使程式設計師能夠訪問當前呼叫的實參

before swap():  i: 10  j: 20

after swap(): i: 20  j: 10

第二種方法是把引數宣告成引用 例如 swap()可重寫如下 

// rswap() 交換 v1 和 v2 引用的值

void rswap( int &v1, int &v2 )  

main()中 rswap()的呼叫看起來像原來的 swap()呼叫 

rswap( i, j ); 

編譯並執行這程式會顯示 i 和 j 的值已經被正確交換了。

引用引數

把引數宣告成引用, 實際上改變了預設的按值傳遞引數的傳遞機制 。在按值傳遞時 ,函 數操縱的是實參的本地拷貝。 當引數是引用時, 函式接收的是實參的左值而不是值的拷貝, 這意味著函式知道實參在記憶體中的位置, 因而能夠改變它的值或取它的位址。 

什麼時候將乙個引數指定為引用比較合適呢? 像 swap()的情況 它必須將乙個引數改變成指標來允許改變實參的值時就比較合適。 引用引數的第二種普遍用法是向主調函式返回額 外的結果, 第三種用法是向函式傳遞大型類物件 。

作為『 通過引用引數向主調函式返回額外結果』 的函式的乙個例子, 我們來定義乙個被 稱為 look_up()的函式 ,它在整型 vector 中查詢乙個特定的值。 如果找到了該值 則 look_up() 返回乙個指向含有該值的 vector 元素的 iterator 迭代器, 否則 返問乙個指向 vector 最後 乙個元素下一位置的 iterator, 表明該值不存在。 在多次出現的情況下, 指向第一次出現的 iterator 被返回, 此外 look_up()用引用引數 occurs 返回該值出現的次數。

#include

// 引用引數 'occurs' 可以含有第二個返回值

vector::const_iterator look_up(

const vector&vec,

int value,  // 值在 vector 中嗎?

int &occurs )  // 多少次? } 

return res_iter; } 

把乙個引數宣告成引用的第三種情況是在向函式傳遞乙個大型類物件時。 在按值傳遞情 況下 ,整個物件將隨每次呼叫而被拷貝。 儘管按值傳遞對內建資料型別的物件和小型類物件 比較滿意 ,但是對於大型類物件, 它的效率就太低了。 使用引用引數, 函式可以訪問被指定 為實參的類物件 ,而不必在函式的活動記錄中拷貝它 。例如 :

class huge ;

extern int calc( const huge & );

int main()

有人可能希望用引用引數以避免拷貝用作實參的大型類物件 ,同時, 又希望防止函式修改實參的值。 如果引用引數不希望在被呼叫的函式內部被修改, 那麼把引數宣告為 const 型 的引用是個不錯的辦法。 這種方式能夠使編譯器防止無意的改變。 例如 ,下列程式段違反了 foo()的引數 xx 的常量性, 因為 foo_bar()的引數不是 const 型的引用 ,所以我們不能保證 foo_bar()不會改變引數 xx 的值。 這違反了 foo()的引數 xx 的常量性 程式被編譯器標記為錯誤 :

class x;

extern int foo_bar( x& );

int foo( const x& xx )

為使該程式通過編譯, 我們改變 foo_bar()的引數的型別 。以下兩種宣告都是可以接受的; 

extern int foo_bar( const x& );

extern int foo_bar( x ); // 按值傳遞

或者可以傳遞乙個 xx 的拷貝做實參 允許 foo_bar()改變它 ;

int foo( const x &xx )

我們可以宣告任意內建資料型別的引用引數。 例如 ,如果程式設計師想修改指標本身, 而不 是指標引用的物件 ,那麼他可以宣告乙個引數 ,該引數是乙個指標的引用。 例如, 下面是交 換兩個指標的函式 :

void ptrswap( int *&v1, int *&v2 )  

如下宣告 

int *&v1; 

應該從右向左讀 v1, 是乙個引用, 它引用乙個指標, 指標指向 int 型的物件。

引用和指標引數的關係

引用必須被初始化為指向乙個物件 一旦初始化了。 它就不能再指向其他物件 。指標可以指向一系列不同的物件,也可以什麼都不指向。因為指標可能指向乙個物件或沒有任何物件 ,所以函式在確定指標實際指向乙個有效的物件之前不能安全地解引用 dereference 乙個指標。 例如; 

class x;

void manip( x *px )

另一方面 對於指標引數 ,函式不需要保證它指向乙個物件,引用必須指向乙個物件 ,甚至在我們不希望這樣時也是如此。 

class type ;

void operate( const type& p1, const type& p2 );

int main()  

如果乙個引數可能在函式中指向不同的物件 ,或者這個引數可能不指向任何物件 。則必 須使用指標引數  

引用引數的乙個重要用法是: 它允許我們在有效地實現過載操作符的同時 ,還能保證用 法的直觀性。  

陣列引數

在 c++中, 陣列永遠不會按值傳遞. 它是傳遞第乙個元素 ,準確地說是第 0 個 的指標。 

例如, 如下宣告 

void putvalues( int[ 10 ] ); 

被編譯器視為 

void putvalues( int* );

陣列的長度與引數宣告無關, 因此 下列三個宣告是等價的; 

// 三個等價的 putvalues()宣告

void putvalues( int* );

void putvalues( int );

void putvalues( int[ 10 ] ); 

因為陣列被傳遞為指標, 所以這對程式設計師有兩個含義  ;在被調函式內對引數陣列的改變將被應用到陣列實參上而不是本地拷貝上 ,當用作 實參的陣列必須保持不變時 ,程式設計師需要保留原始陣列的拷貝, 函式可以通過把參 數型別宣告為 const 來表明不希望改變陣列元素; 

void putvalues( const int[ 10 ] ); 

陣列長度不是引數型別的一部分, 函式不知道傳遞給它的陣列的實際長度, 編澤器也不知道。 當編譯器對實參型別進行引數型別檢查時, 並不檢查陣列的長度 。例如 

void putvalues( int[ 10 ] ); // 視為 int*  

int main()

引數的型別檢查只能保證 putvalues()的兩次呼叫都提供了 int*型的實參, 型別檢查不能檢驗實參是乙個 10 元素的陣列 。



C 函式的引數傳遞

一 函式未被呼叫前,函式的形參並不占有實際的記憶體空間,也沒有被賦值。只有在被呼叫的時候,才被賦值。函式引數傳遞指的就是形參和實參想結合的過程。二 函式引數傳遞存在兩種形式 1 值傳遞 即函式在發生呼叫時,給形參分配記憶體空間,直接以實參的值初始化形參。函式被賦值後,實參和形參即沒有關係,形參的改變...

c 函式的引數傳遞

搜 函式的引數傳遞 很多時候看到的是函式的三種傳遞方式 那麼這三種引數傳遞方式有沒有什麼共同點呢?或者說它們的實質到底是怎樣的呢?1 寫乙個帶有引數的函式返回值型別 函式名 形參列表 那麼形參列表怎麼寫呢?其實我們可以按照 定義乙個變數的寫法 來寫形參列表,什麼意思呢?定義乙個int型變數 為 in...

c 函式引數傳遞

void reset int num void creset const int num void preset int p void cpreset const int p void rreset int arr 10 void initiaresert initializer listlist ...