欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Windows下C/C可變參數宏實(shí)現技巧

WindowsC/C++可變參數宏實(shí)現技巧

在開(kāi)發(fā)過(guò)程中,有很多階段,每個(gè)階段可能會(huì )注重不同的重點(diǎn),我們可能會(huì )在不同階段讓程序輸出或者打印不同的信息以反應運行的情況,所以我們必須分階段的使得程序輸出我們在每個(gè)階段所要關(guān)心的信息,甚至在最后讓程序不再輸出信息。這就要用到了宏定義!

      我們知道,在linux下很方便的就能實(shí)現可變參數宏的定義,

比如:

#define myprint(fmt, a...)    printf("%s,%s(),%d:" fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##a)就定義了自己的輸出宏,當不必再輸出這些可能是調式,跟蹤,斷言,日志...的信息時(shí),可以再定義宏為空:

#define myprintf(fmt,a...)

這樣,重新編譯后,這些宏引用的地方將全部沒(méi)有語(yǔ)句,從而省去這些開(kāi)銷(xiāo)。

但是,在windows下,一般我們采用的VC6.0,VS2003,VS2005,VS2008(待定)編輯器中自帶的C/C++編譯器并不支持變參宏的定義,gcc編譯器支持,據說(shuō)最新版本的C99也支持。

可以在windows下這樣定義宏:

#define myprint printf

但是,當后期不想再要宏輸出了,只能定義 #define myprint為空,在那些有宏調用的代碼區會(huì )留下類(lèi)似 ("DEBUG:>>%d,%s,%f",idx,"weide001",99.001);這樣的語(yǔ)句,它應該會(huì )被程序運算一次,應該會(huì )像函數參數那樣被壓棧,出棧一次,從而增加了程序的運行開(kāi)銷(xiāo),不是一個(gè)上策。

所以,在windows下需要變通一下,以下四種方式可以作為今后windows下定義可變參宏定義的參考:

      1)引用系統變參函數:

#include <stdarg.h>

 

#ifdef _WIN32

  #define vsnprinf _vsnprintf

  #define vsprinf  _vsprintf

#endif

int my_print(const char* file,cosnt char* fun,const char* line,const char *fmt, ...)

{

  int t = 0;

  char out[1024]="";

  va_list ap;

 

  /*
  Test Env:
    gcc version 3.4.5 20051201 (Red Hat 3.4.5-2)
  Result:
  
使用
vsprintf時(shí):
    
fmt的長(cháng)度大于1024時(shí)出現: 段錯誤
    
fmt的長(cháng)度小于1024時(shí)出現: 正常
  
使用vsnprintf時(shí):
    
fmt的長(cháng)度大于1024時(shí)出現: 多余的字符不保存到out
    
fmt的長(cháng)度小于1024時(shí)出現: 正常
  vsnprintf
的返回值同snprintf很相似

  ---------------------------------------------------
  Test Env:
    Microsoft Windows XP [
版本 5.1.2600]
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
  Result:
  
使用_vsprintf時(shí):
    
fmt的長(cháng)度大于1024時(shí)出現: 段錯誤
    
fmt的長(cháng)度小于1024時(shí)出現: 正常
  
使用_vsnprintf時(shí):
    
fmt的長(cháng)度大于1024時(shí)出現: 多余的字符不保存到out
    
fmt的長(cháng)度小于1024時(shí)出現: 正常
  _vsnprintf
的返回值同_snprintf很相似
   */


  va_start(ap, fmt);

  t = vsnprintf(out, 1024, fmt, ap);

  va_end(ap);

 

  printf("%s,%s(),%d: %s\n", file,fun,line,out);

 

  return (t);

}

#define myprint(str)  my_print(__FILE__,__FUNCTION__,__LINE__,str)

只能輸出一個(gè)字符串參數。啥也別說(shuō)了,這個(gè)肯定很爛,這里主要是記住這個(gè)變參函數的實(shí)現方式。

 

2)新的C99規范支持了可變參數的宏,具體使用如下:

#include   <stdarg.h>

 

#define   myprint(fmt, ...)   printf(fmt,__VA_ARGS__)  

 

3)這個(gè)很雷人:(‘_’來(lái)代替‘,’,否則報錯或者警告實(shí)參太多或者實(shí)參個(gè)數不一致)

#define   _   , 

#define   mysprintf(geter, fmt, args)   sprintf(geter, fmt, args)  

 

{

   char str[128] = "";

   mysprintf(str, "name=%s age=%d weight=%f" _ "weide001" _ 23 _ 57.9);//調用很累,和以往的方式出入太大

}

 

4)使用##

#define   mysprintf(geter)   sprintf##geter 

 

{

  char str[128] = "";

  mysprintf(  (str,"name=%s age=%d weight=%f","weide001",23,57.9)  );//也有點(diǎn)雷人,調用時(shí)多出來(lái)了一層括弧

}

 

可變參數的宏里的 ## 作用

GCC始終支持復雜的宏,它使用一種不同的語(yǔ)法從而可以使你可以給可變參數一個(gè)名字,如同其它參數一樣。例如下面的例子:

#define debug(format, args...) fprintf (stderr, format, args)

但是在debug可變參數為0的時(shí)候,debug("hello \n"),編譯會(huì )出錯,采用這樣的方式:

#define debug(format, ...) fprintf (stderr, format, ##args)就可以

##的用法,文中是這樣解釋的:“這里,假如可變參數被忽略或為空,‘##’操作將使預處理器(preprocessor)去除掉它前面的那個(gè)逗號。””這句話(huà)不明白,是##的新功能,還是原有連接的功能的應用?

其實(shí),## 是粘連符

比如windows #define __Ttext L##text

就是在text前面加上了一個(gè)L

__T("abc")就成了 L"abc"

 

可變參數及可變參數宏的使用

我們在C語(yǔ)言編程中會(huì )遇到一些參數個(gè)數可變的函數,例如printf()這個(gè)函數,這里將介紹可變函數的寫(xiě)法以及原理.

 

* 1. 可變參數的宏

一般在調試打印Debug 信息的時(shí)候, 需要可變參數的宏. C99開(kāi)始可以使編譯器標準支持可變參數宏(variadic macros), 另外GCC 也支持可變參數宏, 但是兩種在細節上可能存在區別.

 

1. __VA_ARGS__

__VA_ARGS__ "..." 傳遞給宏.
#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)

GCC中也支持這類(lèi)表示, 但是在G++ 中不支持這個(gè)表示.

 

2. GCC 的復雜宏

GCC使用一種不同的語(yǔ)法從而可以使你可以給可變參數一個(gè)名字,如同其它參數一樣。

#define debug(format, args...) fprintf (stderr, format, args)

這和上面舉的那個(gè)定義的宏例子是完全一樣的,但是這么寫(xiě)可讀性更強并且更容易進(jìn)行描述。

 

3. ##__VA_ARGS__

上面兩個(gè)定義的宏, 如果出現debug("A Message") 的時(shí)候, 由于宏展開(kāi)后有個(gè)多余的逗號, 所以將導致編譯錯誤. 為了解決這個(gè)問(wèn)題,CPP使用一個(gè)特殊的‘##’操作。

#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

這里,如果可變參數被忽略或為空,‘##’操作將使預處理器(preprocessor)去除掉它前面的那個(gè)逗號。如果你在宏調用時(shí),確實(shí)提供了一些可變參數,GNU CPP也會(huì )工作正常,它會(huì )把這些可變參數放到逗號的后面。

 

4. 其他方法

一種流行的技巧是用一個(gè)單獨的用括弧括起來(lái)的的 "參數" 定義和調用宏, 參數在宏擴展的時(shí)候成為類(lèi)似 printf() 那樣的函數的整個(gè)參數列表。

#define DEBUG(args) (printf("DEBUG: "), printf(args))

 

* 2. 可變參數的函數

寫(xiě)可變參數的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.下面我們寫(xiě)一個(gè)簡(jiǎn)單的可變參數的函數,該函數至少有一個(gè)整數參數,第二個(gè)參數也是整數,是可選的.函數只是打印這兩個(gè)參數的值.
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;
}

在程序中可以這樣調用:
simple_va_fun(100);
simple_va_fun(100,200);

從這個(gè)函數的實(shí)現可以看到,使用可變參數應該有以下步驟:
1)
首先在函數里定義一個(gè)va_list型的變量,這里是arg_ptr,這個(gè)變量是指向參數的指針.
2)
然后用va_start宏初始化變量arg_ptr,這個(gè)宏的第二個(gè)參數是第一個(gè)可變參數的前一個(gè)參數,是一個(gè)固定的參數.
3)
然后用va_arg返回可變的參數,并賦值給整數j. va_arg的第二個(gè)參數是你要返回的參數的類(lèi)型,這里是int.
4)
最后用va_end宏結束可變參數的獲取.然后你就可以在函數里使用第二個(gè)參數了.如果函數有多個(gè)可變參數的,依次調用va_arg獲取各個(gè)參數.

如果我們用下面三種方法調用的話(huà),都是合法的,但結果卻不一樣:
1)simple_va_fun(100);
結果是:100 -123456789(會(huì )變的值)
2)simple_va_fun(100,200);
結果是:100 200
3)simple_va_fun(100,200,300);
結果是:100 200

我們看到第一種調用有錯誤,第二種調用正確,第三種調用盡管結果正確,但和我們函數最初的設計有沖突.下面一節我們探討出現這些結果的原因和可變參數在編譯器中是如何處理的.
* 3.
可變參數函數原理

va_start,va_arg,va_end是在stdarg.h中被定義成宏的,由于硬件平臺的不同,編譯器的不同,所以定義的宏也有所不同,下面以VC++stdarg.hx86平臺的宏定義摘錄如下:

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語(yǔ)言的函數是從右向左壓入堆棧的(設數據進(jìn)棧方向為從高地址向低地址發(fā)展,即首先壓入的數據在高地址). 下圖是函數的參數在堆棧中的分布位置:

低地址       |-----------------------------|<-- &v

                          |n-1個(gè)參數(最后一個(gè)固定參數)|

                          |-----------------------------|<--va_startap指向

                          |n個(gè)參數(第一個(gè)可變參數) |

                          |-----------------------------|

                          |....... |

                          |-----------------------------|
                          |
函數返回地址 |

高地址      |-----------------------------|

1. va_list 被定義為char *
2. va_start
將地址ap定義為 &v+_INTSIZEOF(v),&v是固定參數在堆棧的地址,所以va_start(ap, v)以后,ap指向第一個(gè)可變參數在堆棧的地址
3. va_arg
取得類(lèi)型t的可變參數值,int型為例,va_argint型的返回值:
   j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
4. va_end
使ap不再指向堆棧,而是跟NULL一樣.這樣編譯器不會(huì )為va_end產(chǎn)生代碼.

在不同的操作系統和硬件平臺的定義有些不同,但原理卻是相似的.

* 4. 小結

對于可變參數的函數,因為va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變參數的類(lèi)型和個(gè)數需要在該函數中由程序代碼控制;另外,編譯器對可變參數的函數的原型檢查不夠嚴格,對編程查錯不利.
所以我們寫(xiě)一個(gè)可變函數的C函數時(shí),有利也有弊,所以在不必要的場(chǎng)合,無(wú)需用到可變參數.如果在C++,我們應該利用C++的多態(tài)性來(lái)實(shí)現可變參數的功能,盡量避免用C語(yǔ)言的方式來(lái)實(shí)現.

* 5. 附一些代碼

#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
#define debug(format, args...) fprintf (stderr, format, args)
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

//
使用va... 實(shí)現
void debug(const char *fmt, ...)
{
    int nBuf;
    char szBuffer[1024];
    va_list args;

    va_start(args, fmt);
    nBuf = vsprintf(szBuffer, fmt, args) ;
    assert(nBuf >= 0);

    printf("QDOGC ERROR:%s\n",szBuffer);
    va_end(args);
}

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
可變參數及可變參數宏的使用
C語(yǔ)言的宏中定義可變參數
可變參數的宏定義
iOS開(kāi)發(fā) NSLog使用
iOS:你真的理解你在用的自定義NSLog嗎?
從printf談可變參數函數的實(shí)現
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久