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

时间:2017-06-22 16:18:35

标签: c macros

我继承了一些C代码,我在宏上有点生疏。我们使用libdnet来创建数据包。我有代码执行此操作:

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)

我试图了解调用宏时到底发生了什么。我从this SO question了解为什么有一个do-while(0)循环,但我不明白的是这个宏是否修改了我的数据?它应该像一个函数,但ip_pack_p的最终值存储在哪里?

3 个答案:

答案 0 :(得分:6)

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

当prprocessor扩展你的宏时,它变为:

curl -XPUT -H "Content-Type: application/json" \
    -d '[{"lang": "ENGLISH","fields": [{ "name": "id", "value": 1 }]}]' \
    http://localhost:9090/services/rest/index/my_index/document?login=lorem&key=08762e43getye0042f875e86eaiu687f

整个代码块都包含在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; / dowhy?)中。您提供给while(0)的宏参数将逐字复制到相应宏参数指示的位置。

答案 1 :(得分:1)

代码在编译之前被替换。因此,就像您直接在使用宏的地方编写代码一样。宏不是一个功能,从这个意义上说,你可以" 调用"一个宏。

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

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

答案 2 :(得分:1)

  

在C中调用宏,结果是什么?

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

注意:在将注释更改为空格后,会发生宏文本替换。

考虑int putc(int c, FILE *stream);。实现可以使putc()成为真正的函数或实现为宏。在后一种情况下,int中的结果因为宏是这样设计的。

请考虑以下事项。宏SEMI没有做太多,因为它没有尝试模拟函数并且不会形成可返回的结果

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

OP的宏ip_pack_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); ...