Macro vs Inline in C Programming

In this tutorial, we are going to see Macro vs Inline function in C programming.

You can also read, Embedded interview topics, container_of macro in c, stringizing, and token pasting operator in C, and pointers in C.

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.

Macro vs Inline

The Concept of C Macros

Macros are generally used to define constant values that are being used repeatedly in programs. 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 by the preprocessor.

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 let’s compile it with the flag -save-temps so that pre-processing output (a file with an extension .i ) is produced along with the final executable using the below command.

$ 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 its 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 the program repeatedly. Function-like macros are very beneficial when the same block of code needs to be executed multiple times.

Example

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 a macro to define the logging function. It allows a variable argument list and displays arguments on standard output as per the 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, a character string is logged by TRACE_LOG macro, then multiple arguments of different types are also logged as shown in the second call of TRACE_LOG macro. Variable arguments are supported with the use of “…” in the input argument of macro and ##args in the input argument of macro value.

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 projects having code segregated as per releases of the project. If some part of code needs to be executed for release 1 of the 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, the macro is used commonly like below :

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

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

Example

#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, the string “MACRO TEST1 is defined” is printed and since macro TEST3 is not defined, so “MACRO TEST3 is defined” is not printed.

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. The compiler interprets the inline keyword as a mere hint or requests 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. The 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));

Example

#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

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 requests the compiler to do optimizations for this function to save time by avoiding function call overhead. Whenever calling to the inline function is made, the function call would be replaced by the definition of the inline function.

Inline functions provide the following advantages over macros.

  • Since they are functions so the 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 backslashes.
  • Inline functions have their own scope for variables and they can return a value.
  • Debugging code is easy in the 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 the inline function or there are more function calls, then inlining can cause wastage of space.

Remember, inlining is only a request to the compiler, not a command. The compiler can ignore the request for inlining. The compiler may not perform inlining in such circumstances like :

  1. If a function contains a loop. (for, while, do-while)
  2. If a function contains static variables.
  3. If a function is recursive.
  4. If a function return type is other than void, and the return statement doesn’t exist in function body.
  5. If a function contains switch or goto statement.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
VHDL TutorialsUDS Protocol Tutorials
Product Reviews
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments