C函數參數中的三個點__函數

來源:互聯網
上載者:User

原文連結一:http://hi.baidu.com/wjun520/blog/item/1678a11da07fe68086d6b653.html

C++中有函數重載這種方法,以供我們調用時要可以不確定實參的個數,其實 C 語言也可以,而且更高明。

我們在stdio.h 中可以看到 printf() 函數的原型:

int printf(char * format,...)

事實上,我們如果要寫這樣的函數也可以類似的寫,那麼在定義函數時用上這個符號“ ... ” ,它叫預留位置,喊它 “ 三個點 ” 也可以,只要你願意。那麼我可以這樣定義我的函數:

fun(int a,...)

{   }     

只要上課認真聽了的同學(傻瓜除外)都知道,這是個空函數,它是什麼都不做的,光這樣寫還不行的,具體應該怎樣定義呢。

且聽我介紹3 個小東東:

1、 va_list

2、 va_arg()

3、 va_start()

在學習這3 個小東東之前,我們先回憶一下, C 語言是怎麼操作檔案時,是怎麼樣處理記憶體中的資料的呢。學習檔案操作時,我們提到了“流”的概念,我們用指標指向資料所在的記憶體位址,再一個一個的操作。

學習指標時,我們知道有函數指標這個東東,不是指標函數而是函數打針哦。(呵呵,我的同學如果還記得就當複習一下,不要嫌我囉嗦^_^ )。我們記得程式在執行時,會將函數儲存到記憶體中去。現在深入的講一點點,儲存函數時,參數傳遞的過程是怎樣實現的呢。所謂的形式參數(局部變數)實質上又是什麼呢。把這些問題連起來想想,想通了,你的思維勢如破竹。

在調用函數時,程式同樣會把實參傳入,在函數儲存區儲存起來,如果有很多參數,將一起儲存起來。

 

這時候就要用到va_list 了,這是個類型定義,我們可以把它理解成一個指標,它指向第一個參數的地址。

 

如果,我們這樣定義: va_list pp ;

則pp 就是這樣一種變數,它是指向所有參數中的第一個參數的。它不同於一般的指標變數,它是個複合變數,什麼是複合變數啊。結構體類型的嘛,呵呵。如果 a 是第一個參數,能不能寫成 pp=a 呢。

 

假設我定義了char d[]="ruixin",e[]="gelin"; 我要把 e 的值賦給 d ,能不能寫成 d=e 呢。得用 strcpy() ,是吧。呵呵,一樣的道理,這兒我們也用一個函數來實現,它就是 va_start();

 

如果這樣寫:va_start(pp,a);

那麼pp 就指向第一個參數 a 了,並且可得到 a 的類型 int 。

 

這時候如果有下一個參數,就需要使pp 指向下一個參數,並且得到它的類型。同樣需要使用函數來實現,這個函數是: va_arg()

 

可以這樣寫:va_arg(pp, 類型 ) ,這樣 pp 就指向一個參數,並且可以得到那個參數的類型了。

 

注意。類型非常重要,學過指標的都應該清楚,指標的類型如果弄錯的話,位置正確,取出來的數可能也是亂七八糟的。

 

下面我們看一個簡單的例子:

#include <stdio.h>

#include<stdarg.h>

void fun(int a,...)

{

va_list pp;

int n=1;//使用 n 計量參數個數

va_start(pp,a);

do

{

printf("第 %d 個參數 =%d/n",n++,s);

a=va_arg(pp,int);//使 pp 指向下一個參數,將下一個參數的值賦給變數 a

}

while (a!=0);//直到參數為 0 時停止迴圈

}

main()

{

fun(20,40,60,80,0);

}

 

注意。

一定要有上面兩個檔案包含命令,因為程式中用到的那3個小東東都在那個檔案裡。其實真正意義上應該說那是函數,實質上那不過是兩個宏,呵呵。

什麼是宏,什麼是函數,不是這兒要講的,也和這沒太大關係。寫出來,僅僅是回答一個為什麼……

 

原文連結二:http://blog.csdn.net/joliny/archive/2008/04/28/2340299.aspx

VA_LIST 是在C語言中解決變參問題的一組宏

VA_LIST的用法:     
       (1)首先在函數裡定義一具VA_LIST型的變數,這個變數是指向參數的指標
      (2)然後用VA_START宏初始設定變數剛定義的VA_LIST變數,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數。
       (3)然後用VA_ARG返回可變的參數,VA_ARG的第二個參數是你要返回的參數的類型。
       (4)最後用VA_END宏結束可變參數的擷取。然後你就可以在函數裡使用第二個參數了。如果函數有多個可變參數的,依次調用VA_ARG擷取各個參數。

VA_LIST在編譯器中的處理:
1)在運行VA_START(ap,v)以後,ap指向第一個可變參數在堆棧的地址。
(2)VA_ARG()取得類型t的可變參數值,在這步操作中首先apt = sizeof(t類型),讓ap指向下一個參數的地址。然後返回ap-sizeof(t類型)的t類型*指標,這正是第一個可變參數在堆棧裡的地址。然後用*取得這個地址的內容。
(3)VA_END(),X86平台定義為ap = ((char*)0),使ap不再指向堆棧,而是跟NULL一樣,有些直接定義為((void*)0),這樣編譯器不會為VA_END產生代碼,例如gcc在Linux的X86平台就是這樣定義的。

要注意的是:由於參數的地址用於VA_START宏,所以參數不能聲明為寄存器變數,或作為函數或數群組類型。

使用VA_LIST應該注意的問題:
   (1)因為va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變參數的類型和個數完全在該函數中由程式碼控制,它並不能智能地識別不同參數的個數和類型. 也就是說,你想實現智能識別可變參數的話是要通過在自己的程式裡作判斷來實現的.
    (2)另外有一個問題,因為編譯器對可變參數的函數的原型檢查不夠嚴格,對編程查錯不利.不利於我們寫出高品質的代碼。


小結:可變參數的函數原理其實很簡單,而 VA系列是以宏定義來定義的,實現跟堆棧相關。我們寫一個可變函數的C函數時,有利也有弊,所以在不必要的 場合,我們無需用到可變參數,如果在C++裡,我們應該利用C++多態性來實現可變參數的功能,盡量避免用C語言的方式來實現。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.