c語言不定參數宏,va_start,va_arg的來歷解釋

2021-01-10 慕名蘭

關於INTSIZEOF宏是怎麼來的已經解釋過了, 這裡簡要概述一下,宏定義如下:

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

當sizeof(n)<4時,宏的值就是4,當sizeof(n)>4時,宏的值就是4的倍數。

這裡主要介紹兩個宏va_start和va_arg兩個宏,先說這個va_start。

宏定義如下:

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ),這裡的va_list的類型是

char *,v是輸入的第一個參數。首先在理解這個宏的前提要理解函數的形參是如何存儲,這裡我們做個試驗如下圖所示

形參是存儲在棧裡面的,棧的高地址在上,低地址在下,那麼由上圖可以得到的是,x,y,z在棧中存儲的結構從上到下依次為z,y,x。那麼(va_list)&v的意思就是取第一個參數的地址並強制轉化為char*類型。比如這裡的x的地址為1638116(十進位表示的)被強制轉化為char*,對於_INTSIZEOF(v),若是這裡的v也為x的話,那麼這個宏的值為4。進而兩者相加的值就是1638120。這裡要說明的是地址的類型為char*的話,地址加1就相當於只是加1,比如這裡的1638116 + 4 = 1638120;如果地址的類型是int *那麼加1的意思就是地址+1*4。為什麼???是這樣的:比如一個指針指向一個int的輸入比如int a[10],那麼每一個元素就佔4個字節所以地址+1中的1表示1*4,所謂的指針加幾就是為了向前或者向後移動,那麼移動多少字節就要取決於指針指向的變量的類型了,這樣解釋明白了吧!!!很基礎是吧,比如數組是char型的,那麼加1就是地址純粹的加1了,因為sizeof(char) = 1的嘛。

好了,繼續上面說的得到了地址1638116 + 4 = 1638120,那麼這個1638120不正好就是y的地址值嗎。所以這個宏的功能就是:輸入第n個參數,輸出的就是第n + 1個參數的地址值。再來介紹va_arg宏,它的定義如下:

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ),這裡的t輸入的是變量的類型,比如int,char等等之類的。ap += _INTSIZEOF(t)的意思讓指針ap指向下一個參數的地址,在強調一遍高地址存的是函數越右邊的形參值(因為形參是存在棧中的),比如假設ap指向的是x的地址,要使得ap指向y,那麼由於x是在低地址那麼就要加上x的類型,比如這裡的x類型為int,所以這個時候ap是指向y的了。再減去_INTSIZEOF(t)(注意此時ap還是指向的y)那麼((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))整個地址值就還原到原來的地方,也就是再次指向了x,為什麼又要回來,其實就是為了使得ap指向下一個地址,然後再次回到原來的地址,在經過強制轉化(t *),然後再取值*(t *)的符號「*」。

我們來捋一捋這個過程,假設函數只有x,y,z三個形參,一般先調用va_start使得ap指向了y,然後再調用va_arg,使得ap指向z,由於y的值沒取出來進行計算,那麼,就要把地址還原也就是上面再減去_INTSIZEOF(t),在取出y的值。那麼這裡就有一個問題是x的值並沒用到,別急看完下面的例子就明白怎麼用x的值了。

我們來舉一個例子來應用一下,我們要實現,不管輸入多少個參數,使得函數形參從第二個參數開始起進行累加,如下圖所示:

function函數形參x表示的是有多少個參數,因為在執行va_start的時候就直接使得ap的指針指向了下一個形參的位置,所以可以利用x的值來表示有多少個參數,比如這裡有4個參數,分別為0,1,2,3,4,那麼function的功能就是把這幾個值相加也就是6了。

相關焦點

  • C語言中可變參數的用法
    (一)寫一個簡單的可變參數的C函數   下面我們來探討如何寫一個簡單的可變參數的C函數.寫可變參數的C函數要在程序中用到以下這些宏:   void va_start( va_list arg_ptr, prev_param );   type va_arg(
  • C/C++可變參數函數
    但在某些情況下希望函數的參數個數可以根據需要確定,因此c語言引入可變參數函數。這也是c功能強大的一個方面,其它某些語言,比如fortran就沒有這個功能。典型的可變參數函數的例子有大家熟悉的printf()、scanf()等。二、c/c++如何實現可變參數的函數?
  • C語言的那些小秘密之變參函數的實現
    sizeof(type);  #define va_start(arg, start) arg = (va_list)(((char*)&(start)) + sizeof(start))  int sum(int nr, ...)
  • 數據結構(C語言版)課本源碼| 緒論
    >#include <string.h>#include <stdarg.h> // 提供宏va_list、va_start、va_arg、va_end#include <ctype.h> // 提供isprint原型#include "Status.h"Boolean debug = FALSE; int ReadData(FILE
  • Objective-C的元組實現(JDTuple)
    首先,任意入參這個需求,我們很自然的可以想到使用不定參數函數,它使用一塊連續的內存來保存入參信息,並使用va_start來找到內存的首地址來獲取入參。如:void test(id one, ...) {    va_list list;    va_start(list, one);    id arg = nil;    do {        arg = va_arg(list, id);            } while (arg);    va_end(list);};test([NSObject
  • C語言編程核心要點
    類型C是強類型語言,有short、long、int、char、float、double等build-in數據類型,類型是貫穿c語言整個課程的核心概念。struct、union、enum屬於c的構造類型,用於自定義類型,擴充類型系統。變量變量用來保存數據,數據是操作的對象,變量的變字意味著它可以在運行時被修改。
  • C語言之精華總結
    在C++中有函數重載(overload)可以用來區別不同函數參數的調用,但它還是不能表示任意數量的函數參數。在標準C語言中定義了一個頭文件專門用來對付可變參數列表,它包含了一組宏,和一個va_list的typedef聲明。
  • C 語言實現面向對象第一步--對象模型
    (PS: char[0] 數組是一種 C 語言中常用技巧,通常放在結構體的最後,常用來構成緩衝區。 * vl);    void * (* dtor) (void * this);    //.... clone 等};我們來實現以下new和delete:// 要將參數透傳給對象的構造函數,所以使用 C 語言變長參數// type 是具體的類類型參數
  • 高級C語言程式設計師測試必過的十六道最佳題目+答案詳解
    d;a=3;b=5;c=a,b;d=(a,b);printf("c=%d" ,c);printf("d=%d" ,d);}The output for this program is:(a) c=3 d=3(b) c=5 d=3
  • C/C+中的頭文件及其用法詳細說明書
    C語言具有許多庫,這些庫包含預定義的函數以使編程更加容易。在C語言中,頭文件包含一組預定義的標準庫函數。通過在C預處理指令「 #include」中包含頭文件的請求來在程序中使用頭文件。所有的頭文件都有一個擴展名「 .h」。通過包含頭文件,我們可以在程序中使用其內容。
  • c語言不定參數宏INTSIZEOF的由來
    首先,我聲明一下,這篇文章適合想理解c的人群來看的,當然不是說我特別厲害什麼的,因為只是一個小知識點而已,主要是我是從用戶的角度來看的,我這篇文章在我寫完的時候,發現並沒是沒什麼圖片的,沒有什麼興趣點,只有理論的。若是我來看的話,我也不會去看的。
  • 【經典】把脈printf中的C進階技巧
    C語言函數參數一定會入棧嗎?入棧一定是從右向左嗎?    其實這是一個與平臺和處理器相關的問題,所以需要具體情況具體分析,首先大家要明確函數參數和定義的局部變量大部分都是存在堆棧中(不過也可以通過藉助寄存器來傳遞,比如說之前剖析register關鍵字文章中把局部變量放到寄存器中提高效率),使用完畢以後通過堆棧指針的移動進行自動的釋放。
  • 法語】A1 LEÇON 3 Ça va bien ? 精講筆記(上)
    Alors, c』est : David Delage... 35,, rue Notre-Dame, à Montréal.-Et tu as une adresse e-mail ?-Oui, c』est : ddelage@hotmail.com-Comment va ton ami espagnol ?-Luis ?
  • C語言的那些小秘密之預處理
    二、宏定義  宏定義的使用有兩種形式,一種不帶參數,而另外一種帶參數。  1、不帶參數  格式: #define 標識符 字符串  相信上面這個格式大家並不陌生,下面還是來看看如何使用吧。接下來看看帶參數的宏的使用。  2、帶參數  #define 宏名(參數表) 字符串  注意要點:  (1)宏名和參數的括號間不能有空格  (2)宏替換隻作替換,不做計算,不做表達式求解,這點要尤其注意  (3)函數調用在編譯後程序運行時進行,並且分配內存。
  • 麻辣雞Nicki Minaj 《Va Va Voom》
    I-I-I wanna give you one last chance     If you looking for the main attraction     Just hold on tight and let me do one dance     If you want it, I'm gonna be va