Macros in C Demystified

Discussion in 'C' started by poornaMoksha, Oct 9, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    We will discuss the concept of Macros.

    A Macro is a name given to a piece of code or to a value. Whenever the macro is used in the code, it is replaced by its value at the very first stage (preprocessing stage) of compilation process. They are defined using '#define' directive.

    For example, Consider the following piece of code:

    Code:
    #include<stdio.h> 
      
     #define MYMACRO1   //define first macro 
     #define MYMACRO2   // define second macro 
      
     int main(void) 
     { 
     #ifdef MYMACRO1 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO1 Defined \n"); 
     #endif 
      
     #ifdef MYMACRO2 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO2 Defined \n"); 
     #endif 
         return 0; 
     }
    In the above piece of code, we defined two macros :
    1. MYMACRO1
    2. MYMACRO2
    And in the main() function we tested whether these two macros are defined? If yes, then we printed a statement using printf().

    Why are Macros required



    This question may arise in your mind that why do we require macros? What purpose they can serve?

    Well, I'll give you a very simple situation. Suppose you want to write a code in which you have lots of debugging statements, say something like this :

    Code:
    #include<stdio.h> 
     #include<signal.h> 
     #include<unistd.h> 
     #include<stdlib.h> 
      
     int flag; 
     char *s; 
      
     void func() 
     { 
         printf("\n Calling realloc \n"); 
         char *ptr = realloc(s,10); 
         printf("\n realloc call done, freeing global ptr\n"); 
         free(s);       
       
         if(flag == 0) 
         { 
             unsigned int i = 0; 
             for(;i< (0xFFFFFFFF);i++); 
         } 
         if(ptr) 
         { 
             *ptr = 'a'; 
             printf("\n Updating global ptr\n"); 
             s = ptr; 
         } 
     } 
      
     void sig_handler(int signo)  
     { 
        printf("\n Inside sig_handler\n"); 
      
        flag = 1; 
        if (signo == SIGUSR1) 
            printf("received SIGUSR1\n"); 
        else if (signo == SIGUSR2) 
            printf("received SIGUSR2\n"); 
        else 
            printf("ERR : received signal %d\n", signo); 
         
        func(); 
      
        printf("\n Done with sig_handler()\n"); 
     } 
      
      
     int main(void) 
     { 
         printf("\n Inside main() \n"); 
         if (signal(SIGUSR1, sig_handler) == SIG_ERR) 
             printf("\ncan't catch SIGUSR1\n"); 
         if (signal(SIGUSR2, sig_handler) == SIG_ERR) 
             printf("\ncan't catch SIGUSR2\n"); 
         int i = 0; 
      
         while(i<10)  // Simulate a dummy wait 
         { 
             func(); 
             i++; 
         } 
      
         free(s); 
         printf("\n Exiting main() \n"); 
         return 0; 
     }
    I was using this code earlier some day to write an article on Non-reentrant functions to explain how they can cause damage if used from within signal handlers (please don't run this code because it will crash. It was coded this way to explain the reason of crash).

    Anyways, I used a lot of printf calls as debugging statements. Now, suppose a need arises where I just want to run my program without any debugging statements. What would I do?
    • Will I comment all the printfs??
    • Will I remove all the printfs??
    Well removing a printf completely from code is not a wise decision as you can again require these debugging statements. Commenting them one by one would be a tedious task as number of debugging statements grow.

    OK, for those coding freaks who say commenting is still feasible by writing a separate program that can treat this program as a text file and comment printf statements. What will you do if :
    • I want three level of debugging statements in code. (data flow, stack info, All)
    • This mean, if I turn on one level then printf corresponding to only that level are seen on stdout.
    OK, now give up :)

    Commenting the code is not a elegant solution in this case.

    The answer to above situation is using MACROS.

    I'll rewrite the above code here with macros :

    Code:
    #include<stdio.h> 
     #include<signal.h> 
     #include<unistd.h> 
     #include<stdlib.h> 
      
     int flag; 
     char *s; 
      
     //#define DBG_LVL1 
     //#define DBG_LVL2 
     //#define DBG_LVL3 
      
     void func() 
     { 
     #ifdef DBG_LVL3 
         printf("\n Calling realloc \n"); 
     #endif 
         char *ptr = realloc(s,10); 
     #ifdef DBG_LVL3 
         printf("\n realloc call done, freeing global ptr\n"); 
     #endif 
      
         free(s);       
       
         if(flag == 0) 
         { 
             unsigned int i = 0; 
             for(;i< (0xFFFFFFFF);i++); 
         } 
         if(ptr) 
         { 
             *ptr = 'a'; 
     #ifdef DBG_LVL_3 
             printf("\n Updating global ptr\n"); 
     #endif 
             s = ptr; 
         } 
     } 
      
     void sig_handler(int signo)  
     { 
     #ifdef DBG_LVL2 
        printf("\n Inside sig_handler\n"); 
     #endif 
      
        flag = 1; 
        if (signo == SIGUSR1) 
            printf("received SIGUSR1\n"); 
        else if (signo == SIGUSR2) 
            printf("received SIGUSR2\n"); 
        else 
            printf("ERR : received signal %d\n", signo); 
         
        func(); 
     #ifdef DBG_LVL2 
        printf("\n Done with sig_handler()\n"); 
     #endif 
     } 
      
     int main(void) 
     { 
     #ifdef DBG_LVL1 
         printf("\n Inside main() \n"); 
     #endif 
         if (signal(SIGUSR1, sig_handler) == SIG_ERR) 
             printf("\ncan't catch SIGUSR1\n"); 
         if (signal(SIGUSR2, sig_handler) == SIG_ERR) 
             printf("\ncan't catch SIGUSR2\n"); 
         int i = 0; 
      
         while(i<10)  // Simulate a dummy wait 
         { 
             func(); 
             i++; 
         } 
      
         free(s); 
     #ifdef DBG_LVL1 
         printf("\n Exiting main() \n"); 
     #endif 
         return 0; 
     }
    In the above code I have defined 3 Macros for 3 different levels of debugging statements(though they are commented, meaning that we do not want any debugging statement as of now). Now, for any type of debugging statements we just need to uncomment the corresponding macro and only those printfs will be activated.

    Macros can also be defined on basis of the environment our program will run. Like for example, we can have a piece if code under a macro which is defined only if the program is going to run in 64 bit environment. So like this there are various requirements where macros can easily save the day for us.

    Also, since macros are dealt with compile time only so there is no overhead of macros when the program is run.

    Macro with values



    Until now we discussed what are macros and how are they used. Here we will discuss concept of macros with values.

    Macros can be assigned values. Following is the example :

    Code:
    #define SIZE 10
    The above line defines a macro SIZE with value 10.

    Here is a piece of code where we use the above macro :

    Code:
    #include<stdio.h> 
     #include<stdlib.h> 
      
     #define SIZE 10 
      
     int main(void) 
     { 
         char *ptr  = (char*)malloc(SIZE); 
      
         *ptr = 'a'; 
      
         free(ptr); 
      
         return 0; 
     }
    Macros with values can be used for making the code better maintainable so that in future if we just want to change the value of number of bytes that are being passed in malloc() then only the value of macro SIZE is changed. This comes real handy in the cases where SIZE is being used at various places in the program So one needs to change the value of SIZE at one place and the change gets reflected at all the occurrences in the code.

    Defining a Macro from command line



    Until now we have seen how to define and enable Macro from within the code, now lets discuss how the same purpose can be achieved from command line. Lets go back to the very first program :
    Code:
    #include<stdio.h> 
      
     int main(void) 
     { 
     #ifdef MYMACRO1 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO1 Defined \n"); 
     #endif 
      
     #ifdef MYMACRO2 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO2 Defined \n"); 
     #endif 
         return 0; 
     }
    In this piece of code, I have just removed the definition of macros from within the code. Lets see how we achieve this from command line.

    I compile the code in the following way :

    Code:
    gcc -Wall -D MYMACRO1 -D MYMACRO2 macro.c -o macro
    And the output I get is :

    Code:
    $ ./macro 
      
      MYMACRO1 Defined  
      
      MYMACRO2 Defined
    You can verify this concept by compiling the code again without -D options and you will not get any output.

    Similarly, we can assign a value to a macro through command line and can access that value inside the code. Here is a piece of code explaining this :

    Code:
    #include<stdio.h> 
      
     int main(void) 
     { 
     #ifdef MYMACRO1 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO1 Defined with value[%d]\n",MYMACRO1); 
     #endif 
      
     #ifdef MYMACRO2 // test whether MYMACRO1 is defined? 
         printf("\n MYMACRO2 Defined with value[%d]\n",MYMACRO2); 
     #endif 
         return 0; 
     }
    I compile the code in the following way :

    Code:
    gcc -Wall -D MYMACRO1=2 -D MYMACRO2=4 macro.c -o macro
    And the output I get is :

    Code:
    $ ./macro  
      
      MYMACRO1 Defined with value[2] 
      
      MYMACRO2 Defined with value[4]
    Hence we see that we can send value based macros too.

    Conclusion



    To Conclude, this article presented a comprehensive explanation of macros, why are they used, where can they be helpful and different techniques to pass them.
     
    lionaneesh likes this.
  2. lionaneesh

    lionaneesh Active Member

    Joined:
    Mar 21, 2010
    Messages:
    848
    Likes Received:
    224
    Trophy Points:
    43
    Occupation:
    Student
    Location:
    India
    Really liked the Debug idea! Will use that one now! :) Nicely written!
     
  3. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    Thanks buddy !!!!!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice