程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C宏系統缺陷,c宏缺陷

C宏系統缺陷,c宏缺陷

編輯:C++入門知識

C宏系統缺陷,c宏缺陷


這兩天稍稍看了一下boost的preprocessor庫,發覺boost那幫瘋子竟然利用各種奇技淫巧定義出各種數據類型和結構,還在上面定義出加減乘除等等各種運算,在快速浏覽的過程中,還瞄到了很眼熟的各種宏名:list,cons,fold_left、fold_right,估計這幫人把函數式語言的很多特性也搬上去。   本著造輪子練本領的原則,我也嘗試自己去實現各種元素,可是智商不夠,越寫越難受,最後無疾而終。   大致總結了一下,暫時發現C的宏有以下反直覺的缺點:   1、無法定義局部變量,所有宏必須在最外層定義,致使全局可見      而且沒有類似namespace的功能,命名時超頭疼       不支持多行出寫,若要多行需在每行末端加 "\"   2、無控制流,要實現循環、選擇非常麻煩   3、傳參機制反直覺,正常語言的傳參一般采用應用序,先完全展開參數再傳入,而C的宏參數展開過程中若遇到#或##就停止展開,如:        
1      #define BOOL(n)      BOOL##n
2      #define BOOL0         0
3      #define BOOL1         1
4      #define BOOL2         1
5      #define BOOL3         1

 

    BOOL(n)可獲取值n的真假值  
1 #define IF(c, x, y)       IF##c(x, y)
2 #define IF0(x, y)         y
3 #define IF1(x, y)         x

    上面的宏是想要實現選擇控制,IF中傳入邏輯值c,若c為0則返回y, 若c為1則返回x

    假如按如下調用:  IF( BOOL(3),  "t", "f" ),按直覺此句應生成“t”,     可事與願違,   因為展開BOOL(3)時碰到##,所以直接返回BOOL3,     結果上面的宏就變成了 IF( BOOL3,  "t", "f"),按IF宏體繼續展開, 則變成了 IFBOOL3("t", "f")     最後預處理器報錯: 找不到IFBOOL3       因此,為了能正確地把參數BOOL(3)展開為1,還需要多包裝多一層宏:     
1 #define IF(c, x, y)       IF_C(c, x, y)   
2 #define IF_C(c, x, y)   IF##c(x, y)

    這樣,IF( BOOL(3),  "t", "f" )就會先展開參數,變成 IF( BOOL3,  "t", "f"),

    然後宏體展開, IF_C( BOOL3, "t", "f" ),展開參數成了, IF_C( 1, "t", "f" ),     最後才會正確地展開成 IF1( "t", "f" )   4、缺少整數類型,若要利用計數器循環生成代碼時非常麻煩,首先要自己手工定義一堆整數的INC:        
1       #define INC_0 1
2         #define INC_1 2
3         #define INC_2 3
4         #define INC_3 4
5         ……………
6  
7         #define DEC_x x
8         ………………

 

          然後再在INC_xx和DEC_xx之上定義加法,減法,         這樣做相當於需要手工利用最基本的元素構造基本方法,再將這些基本方法不停地復合嵌套,抽象出更高階的函數,         工作量跟創造語言差不多           本來創造語言還是挺有趣的一件事,可由於剛剛提過的反人類反直覺的古怪傳參機制的存在,         致使復合方法構造高階函數的過程異常痛苦,得不時留意參數展開時會不會被#和##打斷,若被打斷則需要增加一層宏來繼續展開。   5、無法實現遞歸,如:        
1    #define x y+1
2      #define y x+1

    則展開x時,先展開成y+1,繼續展開y,x+1+1,這時又碰到了x,預處理器便停止展開了。

      無法實現遞歸,那利用宏實現循環時就變得異常冗長了。     一般來說,while循環和尾遞歸是等價的,所以若支持遞歸,則可用尾遞歸的形式實現循環,但現在不支持,     所以我們需要把尾遞歸的每一步都得親自展開,並將其手工顯示的定義成宏,如:           
1         #define WHLE(...)       WHILE##n(...)
2         #define WHILE0(...)     xxxxx
3         #define WHLE1(....)     WHILE0(......)
4         #define WHLE2(....)     WHILE1(......)
5         #define WHLE3(....)     WHILE2(......)
6         ...................  

 

     這樣做不僅麻煩,而且遞歸深度也只能是一個固定值   6、c的宏只是作簡單的文本替換,所以替換到文本後可能會出現,下面就是一個最經典的例子:     
1       #define square(x)  x*x
2       cout<<square(2+3)<<endl;

        替換後就變成了2+3*2+3,所以寫宏時還要注意在必要的地方加括號。。。。。。。。。

  7、沒辦法傳function-like macro的名字,如:  
1         #define ADD(n, m)   ..........
2         #define FOR(k, op,...)  ......

      若調用FOR((3,3), ADD,...),想要在FOR內部ADD(3,3),發現預處理器會報錯,說ADD沒定義。

      也就是說函數名不能當參數傳入,當然我發現boost裡面是可以的,估計是用了什麼奇技淫巧,沒耐性看,各位大神知道的話請指點以下。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved