由于在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);
}