天天看點

C語言中可變參數函數的使用

由于在C語言中沒有函數重載,解決不定數目函數參數問題變得比較麻煩;即使采用C++,如果參數個數不能确定。也很難采用函數重載。對這種情況,有些人采用指針參數來解決問題。本文就C語言中對不定參數函數的使用方法進行小結。

1、函數參數在堆棧中的分布

位址從高到低,依次是:函數參數清單,函數傳回位址,函數執行代碼段。堆棧中,各個函數的分布情況是倒序的,即最後一個參數在清單中位址最高部分,第一個參數在清單位址的最低部分。

2、VA_LIST宏是在C語言中解決變參問題的一組宏,在C語言的stdarg.h頭檔案中有一下幾個定義:

(1)_INTSIZEOF 宏,擷取類型占用的空間長度,最小占用長度為int的整數倍

 #define _INTSIZEOF(n)   ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

(2)VA_START宏,擷取可變參數清單的第一個參數的位址(ap是類型為va_list的指針,v是可變參數最左邊的參數

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 

(3)VA_ARG宏,擷取可變參數的目前參數,傳回指定類型并将指針指向下一參數(t參數描述了目前參數的類型)

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

(4)VA_END宏,清空va_list可變參數清單

#define va_end(ap)    ( ap = (va_list)0 ) 

3、VA_LIST使用方法:

(1)首先在函數裡定義一具VA_LIST型的變量,這個變量是指向參數的指針;

(2)然後用VA_START宏初始化變量剛定義的VA_LIST變量;

(3)然後用VA_ARG傳回可變的參數,VA_ARG的第二個參數是你要傳回的參數的類型(如果函數有多個可變參數的,依次調用VA_ARG擷取各個參數);

(4)最後用VA_END宏結束可變參數的擷取。

4、使用可變參數需要注意的問題:

(1)可變參數的類型和個數完全由程式代碼控制,它并不能智能地識别不同參數的個數和類型;

(2)如果我們不需要一一詳解每個參數,隻需要将可變清單拷貝至某個緩沖,可用vsprintf函數;

(3)因為編譯器對可變參數的函數的原型檢查不夠嚴格,對程式設計查錯不利。不利于我們寫出高品質的代碼。

(4) 可變參數是由宏實作的,但是由于硬體平台的不同,編譯器的不同,宏的定義也不相同。

5、例子:

(1)列印字元串

#include "stdafx.h"

#include <iostream>

#include <string>

#include <stdarg.h>

using namespace std;

void printstrings(int num,...)

{

va_list arg_ptr;

va_start(arg_ptr,num);

while (num--)

{

printf("/n%s",va_arg(arg_ptr,char*));

}

va_end(arg_ptr);

}

int main(int argc, char* argv[])

{

printstrings(7,"hello","this","is","uncertain","number","parameter","example");

return 0;

}

(2)求和

#include "stdafx.h"

#include <iostream>

#include <string>

#include <stdarg.h>

using namespace std;

int sum(int num,...)

{

int result=0;

va_list arg_ptr;

va_start(arg_ptr,num);

while (num--)

{

result+=va_arg(arg_ptr,int);

}

va_end(arg_ptr);

return result;

}

int main(int argc, char* argv[])

{

int result;

cout<<"sum of 1,2,3,4,5,6"<<endl;

result=sum(6,1,2,3,4,5,6);

cout<<result<<endl;

cout<<"sum of 1,2,3"<<endl;

result=sum(3,1,2,3);

cout<<result<<endl;

return 0;

}

(3)使用vsprintf将可變參數全部寫入緩沖區

函數聲明:int   vsprintf(char   *buf,const   char   *format,   va_list   arglist);    

函數用途:該函數作用同sprintf函數,差別是參數表由一個va_list類型的指針代替  

頭檔案  :stdio.h   stdarg.h  

下例摘自S3C2440中的序列槽列印函數

void Uart_SendByte(int data)

{

if(whichUart==0)

{

if(data=='/n')

{

while(!(rUTRSTAT0 & 0x2));

// Delay(1); //because the slow response of hyper_terminal

WrUTXH0('/r');

}

while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty.

// Delay(1);

WrUTXH0(data);

}

else if(whichUart==1)

{

if(data=='/n')

{

while(!(rUTRSTAT1 & 0x2));

//Delay(1); //because the slow response of hyper_terminal

rUTXH1 = '/r';

}

while(!(rUTRSTAT1 & 0x2)); //Wait until THR is empty.

//Delay(1);

rUTXH1 = data;

}

else if(whichUart==2)

{

if(data=='/n')

{

while(!(rUTRSTAT2 & 0x2));

//Delay(1); //because the slow response of hyper_terminal

rUTXH2 = '/r';

}

while(!(rUTRSTAT2 & 0x2)); //Wait until THR is empty.

//Delay(1);

rUTXH2 = data;

}

}

void Uart_SendString(char *pt)

{

while(*pt)

Uart_SendByte(*pt++);

}

void Uart_Printf(char *fmt,...)

{

va_list ap;

char string[256];

va_start(ap,fmt);

vsprintf(string,fmt,ap);

Uart_SendString(string);

va_end(ap);

}

繼續閱讀