最近,我浏览了JOS Kernel的代码(在麻省理工学院开发,主要是为了帮助像我这样的初学者),并提出了一个小小的疑问,我认为这可能是微不足道的,但无法理解,所以在这里发帖寻求帮助。。
这是一段来自“.c”文件的小代码:-

if(n>0)
           {
            nextfree = ROUNDUP((char *) nextfree, PGSIZE);
            result=nextfree;
            nextfree+=n;
            PADDR(nextfree);
           }

相应的“.h”文件:-
/* This macro takes a kernel virtual address -- an address that points above
* KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
* and returns the corresponding physical address.  It panics if you pass it a
* non-kernel virtual address.
*/

    #define PADDR(kva)                      \
    ({                              \
    physaddr_t __m_kva = (physaddr_t) (kva);        \
    if (__m_kva < KERNBASE)                 \
    panic("PADDR called with invalid kva %08lx", __m_kva);\
    __m_kva - KERNBASE;                 \
    })

现在我有两个关于上述结构的问题-
我们不应该把PADDR(nextfree)的值赋给像var=PADDR(nextfree)这样的变量,而不是像上面那样直接调用它吗怎么办?
为什么有人更愿意在头文件中编写如此小而复杂的定义,而不是为指定的任务创建一个易于理解的函数。

最佳答案

当调用宏时,编译器会在此时将宏定义替换到代码中。不存在这样的“返回值”,除非宏碰巧扩展到具有返回值的对象。
此结构:

 ( { /* ... */ } )

是一个特定于gcc的扩展名,称为“语句表达式”,记录为here。它由括在圆括号中的复合语句组成,并生成最后一个表达式的值。(如果; }之前的最后一个东西不是表达式,则整个东西不会产生值。)
PADDR()宏接受一个内核虚拟地址kva并生成相应的物理地址。如果虚拟地址无效,它将崩溃。(它本可以作为函数编写,但作者选择使用宏,可能是为了提高效率。inline函数可能实现了相同的目标。)
在您显示的代码中,使用PADDR
if (n > 0) {
    /* snip */
    PADDR(nextfree);
}

将调用PADDR宏,但它产生的值将被丢弃假设这不是一个错误,如果nextfree不是有效的虚拟地址,那么这样做可能是为了强制死机。代码不使用产生的物理地址,因为它不需要它;检查就是它所需要的全部。
它仍然会计算__m_kva - KERNBASE;,这可能有点浪费,但我怀疑成本是很高的——优化编译器可能会认识到结果没有被使用,并放弃计算。

10-04 17:25