在C中调用宏,结果如何?

在C中调用宏,结果如何?,c,macros,C,Macros,我继承了一些C代码,对宏有点生疏。我们使用创建数据包。我有这样做的代码: ip_pack_hdr( /* hdr = */ &(pkt->ip), /* tos = */ 0, // Fixed /* len = */ ICMP4_ECHO_PKT_LEN_NO_ETH + data_len, // Fixed /* id = */ 0, // Dynamic (self)

我继承了一些C代码,对宏有点生疏。我们使用创建数据包。我有这样做的代码:

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
);
libdnet中ip_pack_hdr的定义是

#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)

我试图理解调用宏时到底发生了什么。我理解为什么会有do-while(0)循环,但我不理解的是,这个宏是否会就地修改我的数据?它应该像函数一样工作,但ip包的最终值存储在哪里?

在编译之前,代码会被替换。这就好像你直接在使用宏的地方写代码一样。宏与函数不同,从这个意义上讲,您不能“调用”宏

do{}while(0)
只是一种简单的封装代码块的方法,请注意,它永远不会循环,只执行一次。它还创建一个作用域,并允许一个
放在宏之后,因此它看起来像函数调用,但实际上不是


此外,代码在一行中展开,这使得调试非常困难。您可以通过使用适当的标志调用编译器来查看预处理的结果,检查生成的文件或代码,这样您会更好地理解它。

编译前代码会被替换。这就好像你直接在使用宏的地方写代码一样。宏与函数不同,从这个意义上讲,您不能“调用”宏

do{}while(0)
只是一种简单的封装代码块的方法,请注意,它永远不会循环,只执行一次。它还创建一个作用域,并允许一个
放在宏之后,因此它看起来像函数调用,但实际上不是

此外,代码在一行中展开,这使得调试非常困难。通过使用适当的标志调用编译器,您可以看到预处理的结果,检查生成的文件或代码,您将更好地理解它。

没有“调用”宏的事情,因为宏在编译时展开,而调用(如果有的话)发生在运行时。预处理器是C编译器的第一个状态,它扩展宏以供C编译器的翻译阶段使用

当prprocessor展开宏时,它会变成:

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;
整个代码块被包装在
do
/
中,而(0)
()。您提供给
ip\u pack\u hdr
的宏参数会被逐字复制到相应宏参数所指示的位置。

没有“调用”宏这样的事情,因为宏在编译时展开,而调用(如果有)发生在运行时。预处理器是C编译器的第一个状态,它扩展宏以供C编译器的翻译阶段使用

当prprocessor展开宏时,它会变成:

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;
整个代码块被包装在
do
/
中,而(0)
()。您提供给
ip_pack_hdr
的宏参数将被逐字复制到相应宏参数指示的位置

在C中调用宏,结果如何

宏是文本替换。结果取决于它是如何形成的

注意:宏文本替换发生在注释变为空白之后

考虑
intputc(intc,文件*stream)。实现可以使
putc()
成为真正的函数,也可以作为宏实现。在后一种情况下,由于宏是按这种方式设计的,因此结果为
int

考虑以下几点。宏
SEMI
的作用不大,因为它不尝试模拟函数,也不形成可返回的结果

#define SEMI(a) ;
int main() {
  SEMI(nothing)
}
OP的宏
ip\u pack\u hdr()

ip_pack_hdr(
/* hdr = */  &(pkt->ip),
/* tos = */  0,                 // Fixed
/* len = */  ICMP4_ECHO_PKT_LEN_NO_ETH + data_len, // Fixed ...
....
变成一条长线

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); ...
在C中调用宏,结果如何

宏是文本替换。结果取决于它是如何形成的

注意:宏文本替换发生在注释变为空白之后

考虑
intputc(intc,文件*stream)。实现可以使
putc()
成为真正的函数,也可以作为宏实现。在后一种情况下,由于宏是按这种方式设计的,因此结果为
int

考虑以下几点。宏
SEMI
的作用不大,因为它不尝试模拟函数,也不形成可返回的结果

#define SEMI(a) ;
int main() {
  SEMI(nothing)
}
OP的宏
ip\u pack\u hdr()

ip_pack_hdr(
/* hdr = */  &(pkt->ip),
/* tos = */  0,                 // Fixed
/* len = */  ICMP4_ECHO_PKT_LEN_NO_ETH + data_len, // Fixed ...
....
变成一条长线

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); ...

C中的“宏”是一个简单的字符串替换(包括参数替换),由C预处理器(在Linux上,即
cpp foo.C
)在编译完成之前完成。只需在源代码上运行C预处理器,即可查看宏是如何展开的。它并不完全像一个函数。如上所述,宏没有被“调用”。@EugeneSh.:宏什么时候被“调用过”?该宏正在招惹麻烦,而强制转换已经被调用了。不要太喜欢宏@Olaf“天是蓝的,它不是红的”,并不意味着它永远是红的。C中的“宏”是一个简单的字符串替换(包括参数替换),由C预处理器(在Linux上,即
cpp foo.C
)在编译完成之前进行。只需在源代码上运行C预处理器,即可查看宏是如何展开的。它并不完全像一个函数。如上所述,宏没有被“调用”。@EugeneSh.:宏什么时候被“调用过”?该宏正在招惹麻烦,而强制转换已经被调用了。不要太喜欢宏@Olaf“如果天空是蓝色的,它就不是红色的”,这并不意味着它永远是红色的。非常感谢,这确实帮助我理解这一点。宏扩展是否包括
/*hdr=*/
?也许不是。@chux这是一个很好的问题-我几乎可以肯定预处理器会删除所有注释,包括宏扩展中的注释。我必须试着看看它是否发生了。嗯,我认为C会删除注释并用WhitePac替换它们