學完 if-else
, while
, for
之後我們已經可以解決許多問題惹。作為程序流程的最後一篇,今天介紹一個幾乎很少使用的語法:goto
.
如果妳有寫過 batch 或 bash script 的經驗,應該不會對 goto
感到陌生。我們可以在程式碼當中的任意處加上 label:
, 再用 goto label;
跳轉到該label:
. 乍看之下很方便,但是在程式碼當中大量濫用 goto
將會肇致許多麻煩,因此被多數人視為洪水猛獸,包括 MISRA 等機構建議不要使用,有些人或許甚至沒學過。
事實上,if-else
, while
, for
等程序流程在編譯後,於組合語言當中,也都是各式各樣的跳轉指令 (jxx), goto
就是太過於底層一些。然而,偶而使用 goto
還是可以帶來好處。比如先前我們介紹 break;
時舉的線性搜尋數列的例子,如果今天變成二為版本呢??break;
一次只能跳離一個迴圈,因此我們需要多用一個變數:
#include <iostream>
using namespace std;
int main()
{
int n, m, k;
cin >> n >> m >> k;
for (int i = 0; i < n; i++)
{
bool found = false;
for (int j = 0; j < m; j++)
{
int a;
cin >> a;
if (a == k)
{
cout << i << ' ' << j << '\n';
found = true;
break;
}
}
if (found)
break;
}
return 0;
}
如果我們善用 goto
:
可以看到我們省掉一個變數 found
與 \(O(n)\) 次 if
. 當然這兩個例子因為很單純,其實能直接 return 0;
, 以後遇到更複雜的狀況,也可以另外寫個函式 return
, 只是另外寫函式還要考慮傳值的問題。俗話說水能載舟亦能覆舟,goto
提供的方便與帶來的隱憂,這就是自己要取捨的部分惹。
成大教授好文推介。
變數作用域#
在程序流程這章中,我們介紹許多用大括弧包起來的區塊陳述。我們看過在 for
中宣告變數:for (int i = 0; i < n; i++)
, 那麼我們可以在迴圈外使用 i
這個變數嗎??
那如果我們把變數宣告在大括弧之外呢??沒錯,我們可以把變數宣告為與 main()
同一階層,是為全域變數。全域變數很方便沒錯,許多競賽選手都喜歡大量使用,但這在業界也是同樣不被歡迎的。當然有時我們非得用全域變數不可,比如宣告很大的陣列或是遞迴很深時,因為全域變數儲存在 heap 而非 stack.
最後注意 C/C++ 可以在不同的區塊宣告同名的變數,這可能會導致麻煩,比如在 main()
宣告 int x;
再 while
中又宣告 int x;
, 這時在 while
中的 x
都跟外面的 x
毫無關係,不可不慎。
#include <iostream>
using namespace std;
int main()
{
int x = 87;
do
{
int x;
x = 69;
} while (false);
cout << x << '\n';
return 0;
}
Comments