【问题标题】:Access returning value from a function like macro从宏等函数访问返回值
【发布时间】:2013-10-15 23:33:10
【问题描述】:

最近,我正在查看 JOS Kernel 的代码(在 MIT 开发,主要是为了帮助像我这样的初学者)并提出一个小疑问,我认为这可能是微不足道的,但无法弄清楚所以在这里发布寻求帮助..

这是来自 ".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. 为什么有人更愿意在头文件中编写如此小而复杂的定义,而不是为指定任务创建一个易于掌握的函数。

【问题讨论】:

  • 看起来宏对你没有任何用处;内联定义往往用于优化性能,而不是“普通人看”。

标签: c macros c-preprocessor header-files


【解决方案1】:

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

这个结构:

 ( { /* ... */ } )

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

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

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

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

PADDR 宏被调用,但它产生的值被丢弃。假设这不是错误,如果nextfree 不是有效的虚拟地址,这可能是为了强制恐慌。代码不使用生成的物理地址,因为它不需要它;检查就是它所需要的。

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

【讨论】:

  • the PADDR macro is invoked, but the value it yields is discarded. Assuming this isn't an error, this is probably done to force a panic if nextfree is not a valid virtual address. .. 这正是我想要的 .. 谢谢。是的,它在这里用于相同的目的。
【解决方案2】:

嗯...取消选择这个:

  1. 注意括号。这意味着它里面的任何东西都会减少到一个值......
  2. 注意大括号:我们在这里定义了一个新的块作用域。
  3. 现在我们处于新的作用域中,我们可以安全地定义我们喜欢的任何变量,因为它们不会逃脱这个块。
  4. 最后一条语句由一个表达式组成。
  5. 因此,作用域的值就是最后一条语句中该表达式的值。
  6. 因此宏的返回值为__m_kva - KERNBASE

所以回答问题 1:它确实返回一个值,但在您的示例 sn-p 中,这个返回值根本没有被使用。不过,可能其他调用宏的代码确实使用了该值。回答问题 2:这取决于。你可以用宏做一些你不能用函数做的事情,比如undefining他们。在这种情况下,程序员似乎需要复制常见的错误检查,并在示例 sn-p 中使用翻译宏来完成。 (它会提前检查下一个空闲是否会无意中尝试free 用户空间内存或类似的东西。)

【讨论】:

    猜你喜欢
    • 2018-06-08
    • 2011-02-21
    • 2010-12-28
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多