≡ Menu

How to Use C Macros and C Inline Functions with C Code Examples

Many C and C++ programming beginners tend to confuse between the concept of macros and Inline functions.

Often the difference between the two is also asked in C interviews.

In this tutorial we intend to cover the basics of these two concepts along with working code samples.

1. The Concept of C Macros

Macros are generally used to define constant values that are being used repeatedly in program. Macros can even accept arguments and such macros are known as function-like macros. It can be useful if tokens are concatenated into code to simplify some complex declarations. Macros provide text replacement functionality at pre-processing time.

Here is an example of a simple macro :

 #define MAX_SIZE 10

The above macro (MAX_SIZE) has a value of 10.

Now let’s see an example through which we will confirm that macros are replaced by their values at pre-processing time. Here is a C program :

#include<stdio.h>

#define MAX_SIZE 10

int main(void)
{
   int size = 0;
   size = size + MAX_SIZE;

   printf("\n The value of size is [%d]\n",size);

   return 0;
}

Now lets compile it with the flag -save-temps so that pre-processing output (a file with extension .i ) is produced along with final executable :

$ gcc -Wall -save-temps macro.c -o macro

The command above will produce all the intermediate files in the gcc compilation process. One of these files will be macro.i. This is the file of our interest. If you open this file and get to the bottom of this file :

...
...
...
int main(void)
{
   int size = 0;
   size = size + 10;

   printf("\n The value of size is [%d]\n",size);

   return 0;
}

So you see that the macro MAX_SIZE was replaced with it’s value (10) in preprocessing stage of the compilation process.

Macros are handled by the pre-compiler, and are thus guaranteed to be inlined. Macros are used for short operations and it avoids function call overhead. It can be used if any short operation is being done in program repeatedly. Function-like macros are very beneficial when the same block of code needs to be executed multiple times.

Here are some examples that define macros for swapping numbers, square of numbers, logging function, etc.

#define SWAP(a,b)({a ^= b; b ^= a; a ^= b;})
#define SQUARE(x) (x*x)
#define TRACE_LOG(msg) write_log(TRACE_LEVEL, msg)

Now, we will understand the below program which uses macro to define logging function. It allows variable arguments list and displays arguments on standard output as per format specified.

#include <stdio.h>
#define TRACE_LOG(fmt, args...) fprintf(stdout, fmt, ##args);

int main() {
int i=1;
TRACE_LOG("%s", "Sample macro\n");
TRACE_LOG("%d %s", i, "Sample macro\n");
return 0;
}

Here is the output:

$ ./macro2
Sample macro
1 Sample macro

Here, TRACE_LOG is the macro defined. First, character string is logged by TRACE_LOG macro, then multiple arguments of different types are also logged as shown in second call of TRACE_LOG macro. Variable arguments are supported with the use of “…” in input argument of macro and ##args in input argument of macro value.

2. C Conditional Macros

Conditional macros are very useful to apply conditions. Code snippets are guarded with a condition checking if a certain macro is defined or not. They are very helpful in large project having code segregated as per releases of project. If some part of code needs to be executed for release 1 of project and some other part of code needs to be executed for release 2, then it can be easily achieved through conditional macros.

Here is the syntax :

#ifdef PRJ_REL_01
..
.. code of REL 01 ..
..
#else
..
.. code of REL 02 ..
..
#endif

To comment multiples lines of code, macro is used commonly in way given below :

#if 0
..
.. code to be commented ..
..
#endif

Here, we will understand above features of macro through working program that is given below.

#include <stdio.h>

int main() {

#if 0
printf("commented code 1");
printf("commented code 2");
#endif

#define TEST1 1

#ifdef TEST1
printf("MACRO TEST1 is defined\n");
#endif

#ifdef TEST3
printf("MACRO TEST3 is defined\n");
#else
printf("MACRO TEST3 is NOT defined\n");
#endif

return 0;
}

Output:

$ ./macro
MACRO TEST1 is defined
MACRO TEST3 is NOT defined

Here, we can see that “commented code 1”, “commented code 2” are not printed because these lines of code are commented under #if 0 macro. And, TEST1 macro is defined so, string “MACRO TEST1 is defined” is printed and since macro TEST3 is not defined, so “MACRO TEST3 is defined” is not printed.

2. The Concept of C Inline Functions

Inline functions are those functions whose definition is small and can be substituted at the place where its function call is made. Basically they are inlined with its function call.

Even there is no guarantee that the function will actually be inlined. Compiler interprets the inline keyword as a mere hint or request to substitute the code of function into its function call. Usually people say that having an inline function increases performance by saving time of function call overhead (i.e. passing arguments variables, return address, return value, stack mantle and its dismantle, etc.) but whether an inline function serves your purpose in a positive or in a negative way depends purely on your code design and is largely debatable.

Compiler does inlining for performing optimizations. If compiler optimization has been disabled, then inline functions would not serve their purpose and their function call would not be replaced by their function definition.

To have GCC inline your function regardless of optimization level, declare the function with the “always_inline” attribute:

void func_test() __attribute__((always_inline));

Inline functions provides following advantages over macros.

  • Since they are functions so type of arguments is checked by the compiler whether they are correct or not.
  • There is no risk if called multiple times. But there is risk in macros which can be dangerous when the argument is an expression.
  • They can include multiple lines of code without trailing backlashes.
  • Inline functions have their own scope for variables and they can return a value.
  • Debugging code is easy in case of Inline functions as compared to macros.

It is a common misconception that inlining always equals faster code. If there are many lines in inline function or there are more function calls, then inlining can cause wastage of space.

Now, we will understand how inline functions are defined. It is very simple. Only, we need to specify “inline” keyword in its definition. Once you specify “inline” keyword in its definition, it request compiler to do optimizations for this function to save time by avoiding function call overhead. Whenever calling to inline function is made, function call would be replaced by definition of inline function.

#include <stdio.h>

void inline test_inline_func1(int a, int b) {
    printf ("a=%d and b=%d\n", a, b);
}

int inline test_inline_func2(int x) {
    return x*x;
}

int main() {

    int tmp;

    test_inline_func1(2,4);
    tmp = test_inline_func2(5);

    printf("square val=%d\n", tmp);

    return 0;
}

Output:

$ ./inline
a=2 and b=4
square val=25
Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • Bob April 17, 2013, 9:09 am

    Cool. Thanks for the valuable information.

  • SeattleC++ April 17, 2013, 10:39 am

    Macro SQUARE above is typical of naive macro usage and demonstrates one of the weaknesses of C macros. What happens to the macro

    #define SQUARE(x) (x*x)

    when you say SQUARE(v + 1)? The macro is expanded to (v + 1*v + 1). The result, which you probably expected to be equal to the arithmetical formula v*v + 2*v + 1 comes out to 2*v + 1.

    Fix this by defining SQUARE as follows

    #define SQUARE(x) ((x)*(x))

    Now SQUARE(v + 1) expands to ((v + 1)*(v + 1)). This is much better, but it won’t save you if you invoke SQUARE with an expression having side effects.

    SQUARE(++i) evaluates to ((++i)*(++i)) when the effect you probably wanted was more like (++i, (i*i)). This expression uses the rather obscure C comma operator, about which more could be written.

    These difficulties with macros are why C has evolved inline functions.

  • Giovanni April 18, 2013, 12:45 am

    It is a REAL PLEASURE to read your newsletter!

    Very good job!

    I hope you get something out of it!

    God bless you!

    best regards

    Giovanni

  • Matias Miguez April 18, 2013, 5:56 am

    Hi, all your posts there are very useful on my work! Thanks a lot!

    Matias (From Argentina)

  • prakash July 15, 2013, 10:24 pm

    Yes, i agree with SeattleC++ please make a change in that particular Macro definition.

    >> #define SQUARE(x) ((x)*(x))

  • Dharshan B M April 10, 2014, 7:02 pm

    Thanks a lot.

    It is very useful and very clear to understand.

  • abhijit September 8, 2014, 10:23 am

    Need on your opinion why following macro-function doesn’t work:

    #define def_1(var) { \
    int s[var]; \
    }

    int main(){

    def_1(2);

    s[0]=1; s[1]=3;
    printf(“s[0]=%d\t s[1]=%d\n”, s[0], s[1]);

    return;
    }

  • bassam October 26, 2014, 1:29 pm

    notice that no space is allowed between the identifier and the parenthesis, as you wrote:
    #define SQUARE(x) (x*x)

    also pay attention to the pitfalls of such writing, i.e:
    int x = (int)SQUARE(a)

    it will be replaced with (int)a*a, means that the casting is applied to the left part only.
    you can think of other pitfalls for sure. thus, it shoud be written as SQUARE(x)((x)*(x))

  • dev March 27, 2015, 3:32 pm

    To SeattleC++: There is nothing “obscure” about the comma operator, unless you’re unwilling to read. The comma is simply a precedence operator performing checkpointing.

    To bassam: Wrong. The macro expansion along with the typecast will look like this: (int)(a*a). Why would the parenthesis go anywhere? It’s part of the macro. And no, no standard or pre-processor handbook will ever tel you that whitespace isn’t allowed between a macro definition and the identifier. That’s stupid.

  • nya January 31, 2016, 10:52 am

    abhijit: Whenever working with debugging macros, it’s a good idea to actually see the preprocessor’s output. I’m on a phone that suspiciously doesn’t like c/ping so this is paraphrased, but what you have is kinda like this:

    void function() {
    {
    int s[2]
    }
    /* do stuff with s */
    }

    The problem is that the curly braces you wrapped around int s[2]; are that variable’s scope. So its scope ends before you actually do anything with it. Probably, you’ve heard the advice to put curly braces around macros so they behave like functions, but if you define a function local variable in function B, then function A which happens to call function B will never be able to safely access that variable… not that macros ever truly behave like functions anyway.

    If you use gcc, I believe -E is the option to print out postprocessed code. Very useful for macro debugging.

  • neeraj April 3, 2016, 1:29 am

    hi
    is the function be inline at the compile time???
    can i see the the replacement in .i file

  • Ryan Choi April 10, 2016, 4:38 am

    OMG. I got stuck on the macro but not it is very clear after reading your example. Awesome job. Seems you are a very good teacher. Thank you so much!