2013-10-15 75 views
1

最近,我正在瀏覽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. 爲什麼有人更喜歡在頭文件中編寫如此小而複雜的定義,而不是爲指定任務編寫一個易於掌握的函數。

+0

它看起來好像宏不會爲你做任何有用的事情;內聯定義傾向於用於優化性能,而不是用於「僅僅看看凡人」。 – Floris

回答

4

當您調用宏時,編譯器會將宏定義替換爲您的代碼。沒有「返回值」,除非宏發生擴展到具有返回值的東西。

該構建:

({ /* ... */ }) 

是特定的gcc擴展稱爲 「語句表達」,記載here。它由用括號括起來的複合語句組成,它產生最後一個表達式的值。(如果; }之前的最後一件事是不是表達式,那麼整個事情不產生的值。)

PADDR()宏接受一個內核虛擬地址kva,併產生相應的物理地址。如果虛擬地址無效,則會發生混亂。 (它可以寫成函數,但作者選擇使用宏,可能是爲了提高效率。一個inline函數可能可以實現相同的目標。)

在你顯示的代碼中使用了PADDR

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

PADDR宏被調用,但它產生的值將被丟棄。假設這不是一個錯誤,如果nextfree不是一個有效的虛擬地址,這可能會導致恐慌。該代碼不使用生成的物理地址,因爲它不需要它;該支票就是它所需要的。

它仍然計算__m_kva - KERNBASE;,這可能有點浪費,但我懷疑成本是否顯着 - 優化編譯器可能會認識到結果未被使用,並放棄計算。

+0

'PADDR宏被調用,但它產生的值被丟棄。假設這不是一個錯誤,如果nextfree不是一個有效的虛擬地址,那麼這可能會導致恐慌。「..那正是我在找的東西..謝謝。 Yesssss在這裏用於相同的目的。 –

1

嗯...這unpicking:

  1. 注意的括號。這意味着任何內部它將減少到一個單一的值...
  2. 注意大括號:我們在這裏定義一個新的塊範圍。
  3. 現在我們進入新的範圍,我們可以安全地定義任何我們喜歡的變量,因爲它們不會逃脫這個塊。
  4. 最後一條語句由一個表達式組成。
  5. 因此,範圍的值是該表達式在最後一條語句中的值。
  6. 因此宏的返回值是__m_kva - KERNBASE

所以回答問題1:它返回一個值,但在你的例子片斷這個返回的值是根本無法使用。不過,其他調用宏的代碼可能會使用該值。回答問題2:這取決於。你可以用宏來做這些事情,你根本無法使用它們,比如undefining。在這種情況下,程序員似乎需要複製常見的錯誤檢查,並使用翻譯宏在示例代碼段中執行此操作。 (它會提前檢查下一個空閒空間是否會無意嘗試free用戶空間內存或類似內容。)