c - 从像宏这样的函数访问返回值

标签 c macros c-preprocessor header-files

最近,我正在研究 JOS 内核的代码(在麻省理工学院开发,主要用于帮助像我这样的初学者的学习目的)并提出一个小疑问,我认为这可能是微不足道的但无法弄清楚所以在这里发布寻求帮助..

这是来自 .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;                 \
    })

现在我有两个关于上述结构的问题-

  1. 我们不应该将 PADDR(nextfree) 的值赋给像 var=PADDR(nextfree) 这样的变量,而不是像上面那样直接调用它。结果如何?

  2. 为什么有人更愿意在头文件中编写如此小而复杂的定义,而不是为指定的任务创建一个易于掌握的函数。

最佳答案

当您调用宏时,编译器会在此时将宏定义替换到您的代码中。没有这样的“返回值”,除非宏恰好扩展为具有返回值的东西。

这个结构:

 ( { /* ... */ } )

是一个特定于 gcc 的扩展,称为“语句表达式”,已记录 here .它由括在括号中的复合语句组成,它产生最后一个表达式的值。 (如果 ; 之前的最后一件事不是表达式,那么整个事情不会产生值。)

PADDR() 宏采用内核虚拟 地址kva 并产生相应的物理 地址。如果虚拟地址无效,它会发生 panic 。 (它本可以写成函数,但作者选择使用宏,可能是为了提高效率。inline 函数可能已经达到相同的目标。)

在您展示的使用 PADDR 的代码中:

if (n > 0) {
    /* snip */
    PADDR(nextfree);
}

PADDR 宏被调用,但它产生的值被丢弃。假设这不是错误,这可能是为了在 nextfree 不是有效虚拟地址时强制引发 panic 。该代码不使用生成的物理地址,因为它不需要它;支票就是它所需要的。

它仍然计算 __m_kva - KERNBASE;,这可能有点浪费,但我怀疑成本是否很高——优化编译器可能会识别出未使用的结果,并丢弃计算。

关于c - 从像宏这样的函数访问返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19392871/

相关文章:

c - (C) 虚拟网络适配器

assembly - 是否可以使用外部宏参数被内部宏使用?

在 C 中的编译时检查类型是结构还是指针?

c - 除非在使用输出重定向时调用显式 fflush 函数,否则线程不会给出输出

c - 将编译代码与 NASM 和 MSVC 链接时 Unresolved reference

c - 避免/减轻每次函数调用后返回值检查的痛苦的方法?

c - 头文件中同名的宏和函数

macros - 如何在宏中匹配 Rust 的 `if` 表达式?

c - #include 带有 GLSL/C 的自定义 IO?

visual-c++ - 使用 MSVC 获取预处理源代码的最快方法是什么?