Hello C macro


概要
C言語ではマクロ定義がよく使われますが、簡単なHello worldプログラムにはファイルに含まれるマクロ定義が含まれていますが、C言語ではマクロ定義はどのような種類に分けられますか?
  • ファイルは
  • を含む
    ファイルは、インストラクション、すなわちincludeインストラクションを含む、include/#include "myfile.h".,ローは、前処理のコンパイル時にファイル名で指定したファイル内容に置き換えられます.
  • マクロ置換
  • マクロ定義、すなわち、defineコマンドは、define名のようにテキストを置き換えます.前処理をコンパイルすると、名前の場所が置換テキスト日に置き換えられ、置換テキストは任意の文字列で、定義点からコンパイルされたファイルが終了するまでの役割ドメインを果たすことができます.
    例:
    #define NULL 0
    #define forever for( ; ; )   //        forever
    #define max(a,b) ((a) > (b) ? (a) : (b))  //       
    #define putc(_ch, _fp) _IO_putc (_ch, _fp)   //      
    #define swap(a,b) do{ unsign long t = a; a =b ; b=t; } while(0) //      ,  do/while,    
  • 条件は
  • を含む.
    コンパイル前処理による条件制御は、コンパイル工程において求められた条件の値に応じて異なるコード手段を選択的に含むことができることを提供するものである.if文には、sizeof、タイプ強制変換または列挙定数を含まない定数整数式が含まれています.式の評価値が0でない場合、#endif、#elifまたは#elseに遭遇するまで、後続の各行が実行されます.特殊な式defined(名前)は、名前が定義されている場合、値は1で、逆は0です.たとえば、
    #if  !defined(HDR)
    #define HDR
    
    /*hdr.h     */
    
    #endif

    以下に、#if/#elif/#else/#endifの総合例を示す.
      
    #if SYSTEM == SYSV 
          #define HDR "sysv.h"
    #elif SYSTEM == BSD
           #define HDR "bsd.h"
    #elif SYSTEM == MSDOS
           #define HDR "msdos.h"
    #else 
           #define HDR "default.h"
    #endif

     
    一般的な事前処理命令
    #define        //         
    #undef         //      
    #include       //      
    #include_next  // #include  ,          
    #if               //           ,    C    if  
    #ifdef              //           ,     ,        
    #ifndef               // #ifdef  ,            
    #elif               // #if, #ifdef, #ifndef    #elif     ,    #elif     ,    C    else-if
    #else               // #if, #ifdef, #ifndef  ,         ,    #else     ,    C    else
    #endif               //#if, #ifdef, #ifndef           .
    defined               // #if, #elif    ,           
    #line              //           
    #              //                     
    ##               //        (token)          
    #pragma               //       
    #warning               //        
    #error               //        
     
    関数と比較
    以上、マクロ定義が関数機能を実現できることが分かるが、関数と比較すると、
  • コードの作成において、比較的複雑な機能は、マクロ定義を採用して実現され、コードは比較的長く、読書に不利であり、関数は意味を明確に表現することができる
  • .
  • コンパイル後のファイルサイズ量では、関数は1部のみ定義され、他の場所は繰り返し呼び出しであり、マクロ定義は置換原則であり、コンパイル前処理後、参照されたマクロ定義名は、すべて置換され、コンパイル後、ファイルが膨張する.例えば、
  • #include<stdio.h>
    
    #define TEST(a,b) printf(#a "<" #b "=%d
    ",(a)<(b)) int main(){ TEST(2,3); TEST(7,8); return 0; }

    コンパイル前処理を採用する後、gcc-E macro.c,結果:
    # 1 "macro.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "macro.c"
    # 1 "/usr/include/stdio.h" 1 3 4
    # 28 "/usr/include/stdio.h" 3 4
    // 。。。   
    int main(){
     printf("2" "<" "3" "=%d
    ",(2)<(3)); printf("7" "<" "8" "=%d
    ",(7)<(8)); return 0; }
     
  • タイプの観点から、マクロ定義は基本的に弱いタイプに属するが、関数は強いタイプであり、それぞれメリットとデメリットがあり、例えば上述したmaxマクロ定義については、基本整数型、浮動小数点型などは比較より大きいものを用いてもよいが、max関数を定義するにはn個が必要である.
  • 性能の観点から、関数呼び出しはスタックアウトスタック、ジャンプなどの処理を必要とし、マクロ定義の効率に対して
  • より低い.
  • まとめ:
  • ツールバーの
    #defineマクロ
    関数#カンスウ#
    コード長
    使用するたびに、マクロコードがプログラムに挿入されます.非常に小さなマクロに加えて、プログラムの長さは大幅に増加します.
    関数コードは1つの場所にのみ表示されます.この関数を使用するたびに、その場所の同じコードが呼び出されます.
    じっこうそくど
    より高速
    関数呼び出し、返される追加のオーバーヘッドが存在します
    オペレータ優先度
    マクロパラメータの評価は、カッコを付けない限り、隣接オペレータの優先度が予想できない結果をもたらす可能性があるすべての周囲式のコンテキスト環境です.
    関数パラメータは、関数呼び出し時に一度だけ評価され、その結果値は関数に渡されます.式の評価結果は予測しやすい.
    パラメータ評価
    パラメータがマクロ定義に使用されると、毎回再評価されます.複数回の評価のため、副作用のあるパラメータは予測不可能な結果を生じる可能性があります.
    パラメータは、関数呼び出しの前に1回のみ評価されます.関数でパラメータを複数回使用しても、複数回の評価プロセスは発生しません.パラメータの副作用によって特別な問題は発生しません.
    パラメータタイプ
    マクロはタイプに関係なく、パラメータの操作が合法であれば、任意のパラメータタイプに使用できます.
    関数のパラメータはタイプに関係します.パラメータのタイプが異なる場合は、タスクが同じであっても、異なる関数を使用する必要があります.
    以上はソース「Cとポインタ」という本ですが、もちろん、マクロ定義をいつ使用するか、関数をいつ使用するかを比較する必要があります.
    よく使う手口
  • ヘッダファイルが
  • を含むことを防止する.
    #ifndef COMDEF_H
    
    #define COMDEF_H
    
      //     
    
    #endif
  • いくつかのタイプを再定義し、さまざまなプラットフォームとコンパイラの違いによるタイプバイト数の違いを防止し、
  • の移植を容易にする.
    typedef signed long int int32; /* Signed 32 bit value */
    typedef signed short int16; /* Signed 16 bit value */
    typedef signed char int8; /* Signed 8 bit value */
  • は、指定アドレス上の1バイトまたはワード
  • を得る.
    #define MEM_B(x) (*((byte *)(x)))
    
    #define MEM_W(x) (*((word *)(x)))
  • インライン関数機能
  • #define MAX(a,b) ((a) > (b) ? (a) : (b))
  • は、構造体メンバー変数のオフセット量またはバイト数
  • を得る.
    #define offsetof(type,member) ((size_t) &((type *) 0) -> member)
    #define ssizeof(type,member)  sizeof(((type *)0)->member)
  • フレキシブルコール関数
  • #define s5(a) supper_ ## a
    #include <stdio.h>
    void supper_printf(const char* p )
    {
    printf("this is supper printf:
    %s
    ",a); } int main() { s5(printf)("hello owrld"); return 0; }

    複雑なマクロ定義
    linuxカーネルコードから、次のように表示されます.
    #define offsetof(type,member) ((size_t) &((type *)0)->member)
    
    #define container_of(ptr,type,member) ({ const typeof( ((type *)0)->member) * mptr =  (ptr); \
    (type *) ((char*) mptr - offset(type,member));})
    
     
    最初のマクロ定義については、次のように分析します.
    type *  p = ( type *)0; //     p,   type,  0
    int maddr = &(p->member);// member     
    size_t s = (size_t) maddr; //      

    では実現機能が強く,TYPEにおけるMEMBERのオフセット量を求める.
    type *  p = ( type *)0; //     p,   type,  0
    typeof(p->member); //    
    const typeof(p->member) *mptr = (ptr); //    ,   ptr
    char * maddr = (char *)mptr//  mptr  
    offset(type,member); //  member type     
    (char*) mptr - offset(type,member) ;//  type   
    

    簡単な分析を図に示します.
        type
            |----------------|        |----------------|        |----------------|        |----------------|
    ptr->| member --    |        |----------------|        |----------------|        |----------------|        |----------------|
    では、ポインタptrが構造体typeのメンバーメンバーメンバーを指す機能を実現する.ポインタptrにより、構造体typeの開始アドレスが返される.