C语言 从宏等函数访问返回值



最近,我正在浏览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;                 
    })

现在我对上述结构有两个问题——

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

  2. 为什么有人更喜欢在头文件中编写如此小而复杂的定义,而不是为易于掌握的指定任务制作函数。

调用

宏时,编译器会在此时将宏定义替换到代码中。没有这样的"返回值",除非宏碰巧扩展到具有返回值的内容。

此构造:

 ( { /* ... */ } )

是特定于 GCC 的扩展,称为"语句表达式",记录在此处。它由括在括号中的复合语句组成,并生成最后一个表达式的值。(如果; }之前的最后一件事不是表达式,则整个事物不会产生值。

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

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

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

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

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

嗯...取消选择这个:

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

所以要回答问题 1:它确实返回了一个值,但在您的示例代码段中,根本不使用此返回值。不过,据推测,调用宏的其他代码确实使用该值。回答问题2:这取决于。你可以用宏做一些你根本无法用函数做的事情,比如undefining它们。在这种情况下,程序员似乎需要复制常见的错误检查,并使用翻译宏在示例片段中执行此操作。(它提前检查下一个免费不会无意中尝试free用户空间内存或类似的东西。

最新更新