Not long ago I was reading an article about C macro and its potential pitfalls and author suggested not to use them more often in the code and he mentioned about some example such as following to expose the problem of macros :
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int increment(){
static int i=45;
i+=5;
return i;
}
int main(int c,char ** v)
{
int x=20;
printf("the maximum number between %d and %d is : %d",x,increment(),MAX(x,increment()));
return 0;
} |
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int increment(){
static int i=45;
i+=5;
return i;
}
int main(int c,char ** v)
{
int x=20;
printf("the maximum number between %d and %d is : %d",x,increment(),MAX(x,increment()));
return 0;
}
If you try to run this code you will see a very interesting result which makes most of the programmer suspicious about accuracy of C programming language, but the problem is not C. It is resides on macros mechanism and the way compiler treat the macros.
I disassembled the complied version and following is output
.file "macroTest.c"
.text
.globl increment
.type increment, @function
increment:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl i.2162(%rip), %eax
addl $5, %eax
movl %eax, i.2162(%rip)
movl i.2162(%rip), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size increment, .-increment
.section .rodata
.align 8
.LC0:
.string "the maximum number between %d and %d is : %d"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $40, %rsp
.cfi_offset 3, -24
movl %edi, -36(%rbp)
movq %rsi, -48(%rbp)
movl $20, -20(%rbp)
movl $0, %eax
call increment
cmpl -20(%rbp), %eax
jl .L4
movl $0, %eax
call increment
movl %eax, %ebx
jmp .L5
.L4:
movl -20(%rbp), %ebx
.L5:
movl $0, %eax
call increment
movl %eax, %edx
movl -20(%rbp), %eax
movl %ebx, %ecx
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
addq $40, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.data
.align 4
.type i.2162, @object
.size i.2162, 4
i.2162:
.long 45
.ident "GCC: (Ubuntu/Linaro 4.7.2-4precise1) 4.7.2"
.section .note.GNU-stack,"",@progbits
As you see there is no clue about macros in compiled code in other words all the macros will be replaced by proper C code during compilation and if you look at assembly code carefully you will see the increament() function called by main() three times rather than two times and the reason is code replacement of compiler.
printf("the maximum number between %d and %d is : %d",x,increment(),MAX(x,increment())); |
printf("the maximum number between %d and %d is : %d",x,increment(),MAX(x,increment()));
In above line after calling MAX macro we send the increment() function to MAX macro whereas the defintion of MAX is :
#define MAX(x,y) ((x) > (y) ? (x) : (y)) |
#define MAX(x,y) ((x) > (y) ? (x) : (y))
and the compiler will translate it to something like :
((x) > (increment()) ? (x) : (increment()) |
((x) > (increment()) ? (x) : (increment())
as you can see we’re going to get two increment() function in MAX and one in printf() statement and thats the reason of having three calling to increment() inside assembly code.
Conclusion
I am not big fan of making tasks convoluted and I personally think for having a lean and working code its good to step aside from mixed code or combined languages when its unnecessary and this inspection shows how complex it could be in case of using an approach without knowing it thoroughly. So next time think more before making your code complex.
Happy coding!