當前位置:首頁 » 編程語言 » c語言變參

c語言變參

發布時間: 2023-03-27 10:36:35

1. c語言可變參數是干什麼的

可變參數是用於調用函數時,不知道參數的個數及類型的一種場合,最經典最常用的是
int
printf(const
char
*format[,
argument,
...]);
它後面的參數類型及數量都是可變的

printf("%d",x);
//一個int參數
printf("%d%d%s\n",x,y,z);
//三個參數,前兩個為int,後面的為char
*

2. linux c變參函數參數類型不同怎麼辦

寫一個簡單的可變參數的C函數

下面我們來探討如何寫一個簡單的可變參數的C函數.寫可變參數的
C函數要在程序中用到以下這些宏:
void va_start( va_list arg_ptr, prev_param );

type va_arg( va_list arg_ptr, type );

void va_end( va_list arg_ptr );
va在這里是variable-argument(可變參數)的意思.
這些宏定義在stdarg.h中,所以用到可變參數的程序應該包含這個
頭文件.下面我們寫一個簡單的可變參數的函數,改函數至少有一個整數
參數,第二個參數也是整數,是可選的.函數只是列印這兩個參數的值.
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
int j=0;

va_start(arg_ptr, i);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf("%d %d\n", i, j);
return;
}
我們可以在我們的頭文件中這樣聲明我們的函數:
extern void simple_va_fun(int i, ...);
我們在程序中可以這樣調用:
simple_va_fun(100);
simple_va_fun(100,200);
從這個函數的實現可以看到,我們使用可變參數應該有以下步驟:
1)首先在函數里定義一個va_list型的變數,這里是arg_ptr,這個變
量是指向參數的指針.
2)然後用va_start宏初始化變數arg_ptr,這個宏的第二個參數是第
一個可變參數的前一個參數,是一個固定的參數.
3)然後用va_arg返回可變的參數,並賦值給整數j. va_arg的第二個
參數是你要返回的參數的類型,這里是int型.
4)最後用va_end宏結束可變參數的獲取.然後你就可以在函數里使
用第二個參數了.如果函數有多個可變參數的,依次調用va_arg獲
取各個參數.
如果我們用下面三種方法調用的話,都是合法的,但結果卻不一樣:
1)simple_va_fun(100);
結果是:100 -123456789(會變的值)
2)simple_va_fun(100,200);
結果是:100 200
3)simple_va_fun(100,200,300);
結果是:100 200
我們看到第一種調用有錯誤,第二種調用正確,第三種調用盡管結果
正確,但和我們函數最初的設計有沖突.下面一節我們探討出現這些結果
的原因和可變參數在編譯器中是如何處理的.

(二)可變參數在編譯器中的處理

我們知道va_start,va_arg,va_end是在stdarg.h中被定義成宏的,
由於1)硬體平台的不同 2)編譯器的不同,所以定義的宏也有所不同,下
面以VC++中stdarg.h里x86平台的宏定義摘錄如下(』\』號表示折行):

typedef char * va_list;

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

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

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

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

定義_INTSIZEOF(n)主要是為了某些需要內存的對齊的系統.C語言的函
數是從右向左壓入堆棧的,圖(1)是函數的參數在堆棧中的分布位置.我
們看到va_list被定義成char*,有一些平台或操作系統定義為void*.再
看va_start的定義,定義為&v+_INTSIZEOF(v),而&v是固定參數在堆棧的
地址,所以我們運行va_start(ap, v)以後,ap指向第一個可變參數在堆
棧的地址,如圖:

高地址|-----------------------------|
|函數返回地址 |
|-----------------------------|
|....... |
|-----------------------------|
|第n個參數(第一個可變參數) |
|-----------------------------|<--va_start後ap指向
|第n-1個參數(最後一個固定參數)|
低地址|-----------------------------|<-- &v
圖( 1 )

然後,我們用va_arg()取得類型t的可變參數值,以上例為int型為例,我
們看一下va_arg取int型的返回值:
j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已經指向下一個參數的地址了.然後返回
ap-sizeof(int)的int*指針,這正是第一個可變參數在堆棧里的地址
(圖2).然後用*取得這個地址的內容(參數值)賦給j.

高地址|-----------------------------|
|函數返回地址 |
|-----------------------------|
|....... |
|-----------------------------|<--va_arg後ap指向
|第n個參數(第一個可變參數) |
|-----------------------------|<--va_start後ap指向
|第n-1個參數(最後一個固定參數)|
低地址|-----------------------------|<-- &v
圖( 2 )

最後要說的是va_end宏的意思,x86平台定義為ap=(char*)0;使ap不再
指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不
會為va_end產生代碼,例如gcc在linux的x86平台就是這樣定義的.
在這里大家要注意一個問題:由於參數的地址用於va_start宏,所
以參數不能聲明為寄存器變數或作為函數或數組類型.
關於va_start, va_arg, va_end的描述就是這些了,我們要注意的
是不同的操作系統和硬體平台的定義有些不同,但原理卻是相似的.

(三)可變參數在編程中要注意的問題

因為va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,
可變參數的類型和個數完全在該函數中由程序代碼控制,它並不能智能
地識別不同參數的個數和類型.
有人會問:那麼printf中不是實現了智能識別參數嗎?那是因為函數
printf是從固定參數format字元串來分析出參數的類型,再調用va_arg
的來獲取可變參數的.也就是說,你想實現智能識別可變參數的話是要通
過在自己的程序里作判斷來實現的.
另外有一個問題,因為編譯器對可變參數的函數的原型檢查不夠嚴
格,對編程查錯不利.如果simple_va_fun()改為:
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
char *s=NULL;

va_start(arg_ptr, i);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s\n", i, s);
return;
}
可變參數為char*型,當我們忘記用兩個參數來調用該函數時,就會出現
core mp(Unix) 或者頁面非法的錯誤(window平台).但也有可能不出
錯,但錯誤卻是難以發現,不利於我們寫出高質量的程序.
以下提一下va系列宏的兼容性.
System V Unix把va_start定義為只有一個參數的宏:
va_start(va_list arg_ptr);
而ANSI C則定義為:
va_start(va_list arg_ptr, prev_param);
如果我們要用system V的定義,應該用vararg.h頭文件中所定義的
宏,ANSI C的宏跟system V的宏是不兼容的,我們一般都用ANSI C,所以
用ANSI C的定義就夠了,也便於程序的移植.

3. C語言調用可變參數的函數如printf之類的,或者自己定義的函數,如何在函數中確定參數的個數呢

printf按照格式化字元串來讀取

printf("%d%s%d",略); %d%s%d為格式

自定義變參函數,自己定義的解析處理,從va_list 裡面取

4. c語言中的sprintf函數

%3d :就是輸出時右對齊,如滾廳果數字長度小於3,則左邊用岩搏空格填充
%6d :就是輸出時右對齊,如果數字長度小於6,則左邊用空格填充

int a=20984,b=48090;
sprintf(str,"%3d%6d",a,b);
由於a的長度為5,所以不用空格了大棗隱,而b的長度為5,但設定的對齊長度為6,所以左為用空格填充一位
故輸出:20984 48090

5. c語言...用法

C語言變參技術
概述

C語言中有一種長度不確定的參數,形如:"…",它主要用在參數個數不確定的函數中,我們最容易想到的例子是printf函數。

原型:

int printf( const char *format [, argument]... );

使用例:

printf("Enjoy yourself everyday!\\n");

printf("The value is %d!\\n", value);

這種可變參數可以說是C語言一個比較難理解的部分,這里會由幾個問題引發一些對它的分析。

注意:在C++中有函數重載(overload)可以用來區別不同函數參數的調用,但它還是不能表示任意數量的函數參數。

問題:printf的實現

請問,如何自己實現printf函數,如何處理其中的可變參數問題? 答案與分析:

在標准C語言中定義了一個頭文件<stdarg.h>專門用來對付可變參數列表,它包含了一組宏,和一個va_list的typedef聲明。一個典型實現如下:

typedef char* va_list;

#define va_start(list) list = (char*)&va_alist

#define va_end(list)

#define va_arg(list, mode)\\

((mode*) (list += sizeof(mode)))[-1]

自己實現printf:

#include <stdarg.h>

int printf(char* format, …)

{

va_list ap;

va_start(ap, format);

int n = vprintf(format, ap);

va_end(ap);

return n;

}

問題:運行時才確定的參數

有沒有辦法寫一個函數,這個函數參數的具體形式可以在運行時才確定?

答案與分析:

目前沒有"正規"的解決辦法,不過獨門偏方倒是有一個,因為有一個函數已經給我們做出了這方面的榜樣,那就是main(),它的原型是:

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

函數的參數是argc和argv。

深入想一下,"只能在運行時確定參數形式",也就是說你沒辦法從聲明中看到所接受的參數,也即是參數根本就沒有固定的形式。常用的辦法是你可以通過定
義一個void
*類型的參數,用它來指向實際的參數區,然後在函數中根據根據需要任意解釋它們的含義。這就是main函數中argv的含義,而argc,則用來表明實際
的參數個數,這為我們使用提供了進一步的方便,當然,這個參數不是必需的。

雖然參數沒有固定形式,但我們必然要在函數中解析參數的意義,因此,理所當然會有一個要求,就是調用者和被調者之間要對參數區內容的格式,大小,有效性等所有方面達成一致,否則南轅北轍各說各話就慘了。

問題:可變長參數的傳遞

有時候,需要編寫一個函數,將它的可變長參數直接傳遞給另外的函數,請問,這個要求能否實現?

答案與分析:

目前,你尚無辦法直接做到這一點,但是我們可以迂迴前進,首先,我們定義被調用函數的參數為va_list類型,同時在調用函數中將可變長參數列表轉換為va_list,這樣就可以進行變長參數的傳遞了。看如下所示:

void subfunc (char *fmt, va_list argp)

{

...

arg = va_arg (fmt, argp); /* 從argp中逐一取出所要的參數 */

...

}

void mainfunc (char *fmt, ...)

{

va_list argp;

va_start (argp, fmt); /* 將可變長參數轉換為va_list */

subfunc (fmt, argp); /* 將va_list傳遞給子函數 */

va_end (argp);

...

}

問題:可變長參數中類型為函數指針

我想使用va_arg來提取出可變長參數中類型為函數指針的參數,結果卻總是不正確,為什麼?

答案與分析:

這個與va_arg的實現有關。一個簡單的、演示版的va_arg實現如下:

#define va_arg(argp, type) \\

(*(type *)(((argp) += sizeof(type)) - sizeof(type)))

其中,argp的類型是char *。

如果你想用va_arg從可變參數列表中提取出函數指針類型的參數,例如

int (*)(),則va_arg(argp, int (*)())被擴展為:

(*(int (*)() *)(((argp) += sizeof (int (*)())) -sizeof (int (*)())))

顯然,(int (*)() *)是無意義的。

解決這個問題的辦法是將函數指針用typedef定義成一個獨立的數據類型,例如:

typedef int (*funcptr)();

這時候再調用va_arg(argp, funcptr)將被擴展為:

(* (funcptr *)(((argp) += sizeof (funcptr)) - sizeof (funcptr)))

這樣就可以通過編譯檢查了。

問題:可變長參數的獲取

有這樣一個具有可變長參數的函數,其中有下列代碼用來獲取類型為float的實參:

va_arg (argp, float);

這樣做可以嗎?

答案與分析:

不可以。在可變長參數中,應用的是"加寬"原則。也就是float類型被擴展成double;char,
short被擴展成int。因此,如果你要去可變長參數列表中原來為float類型的參數,需要用va_arg(argp,
double)。對char和short類型的則用va_arg(argp, int)。

問題:定義可變長參數的一個限制

為什麼我的編譯器不允許我定義如下的函數,也就是可變長參數,但是沒有任何的固定參數?

int f (...)

{

...

}

答案與分析:

不可以。這是ANSI C 所要求的,你至少得定義一個固定參數。

這個參數將被傳遞給va_start(),然後用va_arg()和va_end()來確定所有實際調用時可變長參數的類型和值。

6. C語言函數參數中的...如何使用

#include<stdarg.h>/*必須頭文件,定汪迅義了各種變參宏*/

void困冊此myprint(constchar*format,...)/*和printf一樣*/
{
va_listvlist;//va_list的長度沒有辦法單獨算出來,只能從format格式列表中計算出,通常就是%的個數,或者顯示地指出,比如下面一個例子
va_start(vlist,format);

intsize=vprintf(format,vlist);
va_end(vlist);
}


voidPrintFloats(intn,...)/*需要指定變參個數n*/
{
inti;
doubleval;
printf("Printingfloats:");
va_listvl;
va_start(vl,n);
for(i=0;i<n;i++)
{
val=va_arg(vl,double);
printf("[%.2f]",val);
}
姿慎va_end(vl);
printf(" ");
}

7. C語言的問題,實在是不會做了,求大神!!!

函數在C語言中是必不可少的一部分,大致可以分為兩種:系統定義好的和我們自己寫的。不管是哪種函數,它們都默默地、兢兢業業地完成著屬於自己的任務。只要給它一些參數,它就會把參數處理好、有些函數還會把處理結果返回出來。以下幾種特殊函數,你有見過嗎?會用嗎?

1、靜態函數

普通的函數一般是全局的,可見范圍是跨文件的,比如a.c 這個文件中有一個函數名為 func() , b.c文件 中是可以直接調用a.c中的func函數的。而靜態函數不同,它被static修飾之後可見范圍縮小到本文件可見,其它文件不可見。

如果真的是閑得蛋疼想在一個文件中調用一個別的文件中的靜態函數,也有辦法:

1.直接把func所在的函數包含進來 #include "b.c"

2.可以把static 修飾的函數寫在頭文件中

2、遞歸函數:

這個名字看起來有一點數學味,比較高級的樣子。其實它的定義也很簡單:在一個函數內部調用了自身,那這個函數就是遞歸函數。

下面寫了一個例子:

使用遞歸函數的時候有幾個問題要注意:

1.只有能被遞歸解決的問題才可以使用遞歸來解決。(階乘、冪運算、字元翻轉等)

2.遞歸函數中必須有一個可以直接退出(返回)的條件,否則會進入無限循環。

3.遞歸的過程包含兩個:逐漸遞進,層層回歸。

4.遞歸函數會使用大量的棧內存空間,要注意遞歸的層次不要太深,如果一定要用的話可以嘗試把棧空間的大小臨時設置一下。(ulimit -s)

3.指針函數

這種函數是什麼其實很明顯了,指針函數就是返回值是指針類型的函數。

使用的時候要確保該函數的返回值是指針,不然編譯器會報警告。

4、回調函數

函數的實現方,它不方便直接調用該函數,而是由第三方介面來調用該函數,該函數就是回調函數。概念有點繞,我們直接看代碼:

可以看到我們在主函數main裡面並沒有直接調用func()函數,而是運用函數指針P通過一個signal函數去調用它。在實際工程中回調函數會用得比較多,就像圖中的signal函數,假設這個函數是從別人手上買回來的,我們無權訪問源碼,只能夠使用。賣家給我們留下了介面讓我們使用它,因此我給了它兩個參數,當它收到信號2的時候,我希望它能幫我調用我寫的func()函數。那麼func()就是所謂的回調函數。

5、內聯函數

如果有一個函數被多次調用,那麼該函數在調用過程中會消耗程序執行的時間, 而內聯函數指的就是可以把需要調用的函數副本拷貝到調用者的內部,節約了調用過程中的時間。

普通函數的調用與切換過程:

內聯函數示意圖:

內聯語法:

什麼時候應該用內聯函數:一個函數在另一個函數多次被調用,而且被調用函數的內容比較少的情況。

6、變參函數

如果一個函數的參數類型以及參數的數量都是可變的,那麼這個函數就稱之為變參函數(如我們常用的printf)。

函數形參在棧中分配內存的順序,從右到左的獨特順序使得第一個參數是棧頂元素(即示例中的參數 format),我們就可以根據第一個已知的參數(綠色)所提供的線索, 來回溯剩下的未知的參數(黃色)

以上六種特殊函數,你看懂了嗎?有任何疑問可以評論區告訴我哦!

森式嵌入,持續分享嵌入式硬知識。歡迎志同道合的朋友關注一起交流進步!

8. c語言中的sprintf函數

1.
sprintf
是個變參函數,定義如下:
int
sprintf(
char
*buffer,
const
char
*format
[,
argument]
...
);
除了指碧前兩個參數類型固定外,後面可以接任意多個參數。而它的精華,顯然就在第二個參數:格式化字元串上。
2.
sprintf
使用格式化字元串來指定串的格式,在格式串內部使用一些以「%」開頭的格式說明符(format
specifications)來占據一個位置,在後邊的變參列表中提供相應的變數,最終函數就會用相應大歲位置的變數來替代那個說明符,產生一個調用者想要的字元串。
3.
sprintf
最常見的應用之一莫過於把整數列印到字元串中,所以,spritnf
在大多數唯仿舉場合可以替代itoa。

9. 變參(C語言)

這是一個變參函數聲明。
加三個點就是了。
取得參數的套路慧基是

這樣三隱橋步,就將 各個參數,放在了灶碧猛 buf 中。
完整函數如下:

調用如下:

其中,vsprintf 可能造成內存泄漏,因為傳入的 buf 的大小未知。
可換成

函數原型:

vsprintf 函數

vsnprintf 函數

10. C語言變參函數Printf實現機制是什麼

像printf()/scanf()這樣參數數量可變的函數稱為variadic函數,請自行在網上查找相關資料吧.

熱點內容
安卓7自帶小游戲怎麼玩 發布:2024-11-03 02:17:59 瀏覽:158
csgo如何連伺服器 發布:2024-11-03 02:13:04 瀏覽:391
斯坦福python 發布:2024-11-03 01:47:08 瀏覽:609
乘法編程教程 發布:2024-11-03 01:37:06 瀏覽:693
咒怨2ftp 發布:2024-11-03 01:31:17 瀏覽:432
linux如何安裝telnet 發布:2024-11-03 01:20:20 瀏覽:84
蘋果手機怎麼加密軟體 發布:2024-11-03 01:20:16 瀏覽:886
微信存儲路徑刪除 發布:2024-11-03 01:14:09 瀏覽:393
c語言貪吃蛇源碼 發布:2024-11-03 00:57:15 瀏覽:120
八碼演算法 發布:2024-11-03 00:56:31 瀏覽:975