簡體   English   中英

使用函數參數作為變量

[英]Using function parameter as variable

在函數內部重新分配函數參數是不好的做法還是好的做法,或者是不確定的行為?

讓我用一個例子來說明我要做什么:

void
gkUpdateTransforms(GkNode *node /* other params */) {
  GkNode *nodei;

  if (!(nodei = node->chld))
    return;

  do {
    /* do job */
    nodei = nodei->next;
  } while (nodei);
}

選擇:

void
gkUpdateTransforms2(GkNode *node /* other params */) {

  /* node parameter is only used here to get chld, not anywhere else */
  if (!(node = node->chld))
    return;

  do {
    /* do job */
    node = node->next;
  } while (node);
}

我檢查了程序集的輸出,看起來好像一樣,我們不需要在第二個中聲明一個變量。 您可能會問,如果更改了參數類型,但是對於第一個參數,相同的條件將是相同的,因為它也需要更新。

編輯:參數是按值傳遞,我的意圖不是編輯指針本身

EDIT2:遞歸函數呢? 如果gkUpdateTransforms2是遞歸的,會發生什么? 我很困惑,因為函數會自行調用,但我認為在每次調用中,參數都會不同

我不知道您為什么認為這將是未定義的行為-事實並非如此。 通常,這只是編碼風格的問題,沒有明顯的對與錯。

通常,優良作法是將參數視為不可變對象 保留功能的原始輸入副本是很有用的。 因此,最好使用局部變量,該局部變量只是參數的副本。 如您所見,這絲毫不影響性能-編譯器將優化代碼。

但是,如果您也寫入參數,則沒什么大不了的。 這也是常見的做法。 稱其為不良做法將非常令人討厭。

如果不應該修改某些函數式編碼樣式,則所有函數參數都將變為const ,但我個人認為這只是混淆,這會使代碼更難閱讀。 在您的情況下,這種學究式樣式將為void gkUpdateTransforms(GkNode*const node) 不要與const正確性相混淆,const正確性是一件普遍的事情,而不僅僅是樣式問題。


但是,您的代碼中確實存在某些絕對不正確的做法,那就是在條件內部進行賦值。 盡可能避免這種情況,這很危險,並使代碼更難閱讀。 大多數情況下沒有好處。

在C的歷史早期就注意到混合===的危險。為了解決這一問題,在1980年代,人們想到了諸如“尤達條件”之類的對大腦造成傷害的事物。 然后在1989年左右出現了Borland Turbo C,它具有精美的警告功能“可能錯誤分配”。 那是Yoda條件的死亡,從那時起,編譯器就警告不要指定條件。

確保當前的編譯器對此發出警告。 也就是說,請確保不要使用比1989年的Borland Turbo更差的編譯器。是的,市場上有較差的編譯器。

(gcc給出了“警告:建議將用作真值的賦值括號括起來”)


我將代碼寫為

void gkUpdateTransforms(GkNode* node /* other params */) 
{
  if(node == NULL)
  {
    return ;
  }

  for(GkNode* i=node->chld; i!=NULL; i=i->next;)
  {
    /* do job */
  }
}

這主要是風格上的更改,以使代碼更具可讀性。 它不會大大提高性能。

恕我直言,這不是完全“壞”的做法,但是值得懷疑的是,沒有更好的方法。 關於分析匯編程序輸出:它可能是有趣而有教育意義的外觀,但是不建議您使用它作為優化或更糟的源代碼懶惰的理由。 下一個編譯器或下一個體系結構可能只會使您的想法完全無效-我的建議是在這里與Knuth保持聯系: “與其想象我們的主要任務是指導計算機做什么,不如讓我們專注於向人類解釋我們想讓計算機做什么。”

在您的代碼中,我認為決定是50:50,沒有明確的獲勝者。 我認為節點迭代器有一個自己的概念,可以證明一個單獨的編程結構(在我們的例子中只是一個變量)是合理的,但是函數又是如此的簡單,以至於在下一個方面,我們並沒有贏得太多程序員查看您的代碼,因此我們可以很好地使用第二個版本。 如果您的功能開始隨着時間的推移而變異和增長,則此前提可能會失效,我們最好選擇第一個版本。 也就是說,我會像這樣編寫第一個版本:

void
gkUpdateTransforms(GkNode *node /* other params */) {    
  for (GkNode *nodei = node->chld; nodei != NULL; nodei = nodei->next) {
      /* do job */
  }
}

這是定義明確的,也是實現此行為的完美方法。

您可能將其視為問題的原因是執行以下操作的常見錯誤:

int func(object a) {
    modify a // only modifying copy, but user expects a to be modified

但是對於您的情況,您希望復制該指針。

只要按值傳遞它,就可以將其安全地視為任何其他局部變量。 在這種情況下,這不是一個壞習慣,也不是未定義的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM