Mark
Mark

Reputation: 2148

Calling a macro in C, what is the result?

I have inherited some C code, and I am a bit rusty on macros. We use libdnet to create packets. I have code that does this:

ip_pack_hdr(
    /* hdr = */  &(pkt->ip),
    /* tos = */  0,                 // Fixed
    /* len = */  ICMP4_ECHO_PKT_LEN_NO_ETH + data_len, // Fixed
    /* id = */   0,                 // Dynamic (self)
    /* off = */  IP_DF,             // Fixed
    /* ttl = */  0,                 // Dynamic (caller)
    /* p = */    IP_PROTO_ICMP,     // Fixed
    /* src = */  src.addr_ip,       // Fixed
    /* dst = */  dst.addr_ip        // Fixed
);

and the definition of ip_pack_hdr in libdnet is

#define ip_pack_hdr(hdr, tos, len, id, off, ttl, p, src, dst) do {  \
    struct ip_hdr *ip_pack_p = (struct ip_hdr *)(hdr);      \
    ip_pack_p->ip_v = 4; ip_pack_p->ip_hl = 5;          \
    ip_pack_p->ip_tos = tos; ip_pack_p->ip_len = htons(len);    \
    ip_pack_p->ip_id = htons(id); ip_pack_p->ip_off = htons(off);   \
    ip_pack_p->ip_ttl = ttl; ip_pack_p->ip_p = p;           \
    ip_pack_p->ip_src = src; ip_pack_p->ip_dst = dst;       \
} while (0)

I am trying to understand what exactly happens when the macro is called. I understand from this SO question why there is a do-while(0) loop, but what I don't understand is does this macro modify my data in place? It is supposed to act like a function but where does the final value of ip_pack_p get stored?

Upvotes: 1

Views: 2449

Answers (3)

chux
chux

Reputation: 153377

Calling a macro in C, what is the result?

A macro is text substitution. The result depends on how it if formed.

Note: macro text substitution occurs after comments are changed into white-spaces.

Consider int putc(int c, FILE *stream);. An implementation may make putc() a true function or implement as a macro. In the latter case, the result in an int because the macro was design that way.

Consider the following. The macro SEMI does not do much as it is not trying to emulate a function and does not form a returnable result

#define SEMI(a) ;
int main() {
  SEMI(nothing)
}

OP's macro ip_pack_hdr() simple substitutes

ip_pack_hdr(
/* hdr = */  &(pkt->ip),
/* tos = */  0,                 // Fixed
/* len = */  ICMP4_ECHO_PKT_LEN_NO_ETH + data_len, // Fixed ...
....

becomes one long line

do { struct ip_hdr *ip_pack_p = (struct ip_hdr *)( &(pkt->ip)); ip_pack_p->ip_v = 4; ip_pack_p->ip_hl = 5; ip_pack_p->ip_tos = 0; ip_pack_p->ip_len = htons( ICMP4_ECHO_PKT_LEN_NO_ETH + data_len); ...

Upvotes: 1

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53006

The code gets substituted before compilation. So it's as if you write the code directly where you use the macro. A macro is not like a function, and in that sense you can't "call" a macro.

The do { } while (0) is just a simple a way to enclose the block of code, note that this will never loop, it's just executed once. It also creates a scope, and allows a ; to be placed after the macro, so it looks like a function call, but it's not.

Also, the code is expanded in a single line, which makes debugging very difficult. You can see the result of preprocessing by calling the compiler with the appropriate flag, inspect the generated file or code and you will understand it better.

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726539

There is no such thing as "calling" a macro, because macros are expanded at compile time, while calling, if any, happens at run time. Preprocessor, the first state of C compiler, expands your macro for use by the translating stage of C compiler.

When prprocessor expands your macro, it becomes this:

struct ip_hdr *ip_pack_p = (struct ip_hdr *)(&(pkt->ip));
ip_pack_p->ip_v = 4;
ip_pack_p->ip_hl = 5;
ip_pack_p->ip_tos = 0;
ip_pack_p->ip_len = htons(ICMP4_ECHO_PKT_LEN_NO_ETH + data_len);
ip_pack_p->ip_id = htons(0);
ip_pack_p->ip_off = htons(IP_DF);
ip_pack_p->ip_ttl = 0;
ip_pack_p->ip_p = IP_PROTO_ICMP;
ip_pack_p->ip_src = src.addr_ip;
ip_pack_p->ip_dst = dst.addr_ip;

The entire block of code is wrapped in do/while(0) (why?). Macro arguments that you provided to ip_pack_hdr are copied verbatim into places indicated by corresponding macro parameters.

Upvotes: 6

Related Questions