Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Macros in C Demystified (http://www.go4expert.com/articles/macros-c-demystified-t26890/)

poornaMoksha 9Oct2011 17:42

Macros in C Demystified
 
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 10Oct2011 18:12

Re: Macros in C Demystified
 
Really liked the Debug idea! Will use that one now! :) Nicely written!

poornaMoksha 10Oct2011 19:52

Re: Macros in C Demystified
 
Quote:

Originally Posted by lionaneesh (Post 87843)
Really liked the Debug idea! Will use that one now! :) Nicely written!

Thanks buddy !!!!!


All times are GMT +5.5. The time now is 14:15.