-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathch02.html
1 lines (1 loc) · 32.2 KB
/
ch02.html
1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html></html><head><title>第二章 變數與指定運算子「=」</title><meta charset="UTF-8"/><meta content="text/html; charset=UTF-8" http-equiv="content-type"/><link href="style.css" rel="stylesheet" type="text/css"/><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/><script src="jquery/dist/jquery.min.js"></script><script type="text/javascript" src="./selectchapter.js"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script></head><body><nav class="navbar navbar-default" role="navigation"><div class="container-fluid"><div class="navbar-header"><a class="navbar-brand" href="index.html">板中資訊社</a></div><div><ul class="nav navbar-nav"><li class="active"><a href="#">C++</a></li><li class="dropdown"><a class="dropdown-toggle" href="#" data-toggle="dropdown">課程<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="ch01.html">第一章 立刻動手</a></li><li><a href="ch02.html">第二章 變數與指定運算子「=」</a></li><li><a href="ch03.html">第三章 比較運算子與 if 陳述式</a></li><li><a href="ch04.html">第四章 迴圈</a></li><li><a href="ch05.html">第五章 基礎資料型別</a></li><li><a href="ch06.html">第六章 字元與字串</a></li><li><a href="ch07.html">第七章 陣列</a></li><li><a href="ch08.html">第八章 自定義函數與資料型別</a></li><li><a href="ch09.html">第九章 排序</a></li></ul></li><li class="dropdown"><a class="dropdown-toggle" href="#" data-toggle="dropdown">附錄<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="basic_type.html">A 基礎資料型別</a></li></ul></li></ul></div></div></nav><h1>第二章 變數與指定運算子「=」</h1><button id="button1">變數與輸入運算子</button><button id="button2">多個變數的輸入</button><button id="button3">指定運算子</button><button id="button4">複合指定運算子</button><div class="para" id="para1" style="display:none;"><h2>2.1 變數與輸入運算子</h2><p>前面已經做了兩個題目,都是輸出一個固定的字串。但是這樣的程式好像沒有太大的用處,現實生活中,大部份的程式都需要輸入一些資料,再根據所輸入的資料來做一些處理然後再輸出所得到的資訊。接下來我們就來看第一個需要輸入資料的題目:</p><li>a(href="https://zerojudge.tw/ShowProblem?problemid=d049")d049. 中華民國萬歲!</li><p>這個題目要求我們輸入一個西元年份,把它轉成民國年份後輸出。這題很簡單,因為大家都知道只要把西元年份減掉 1911 就會變成民國年份了。問題是在C++ 的程式中要如何去輸入呢?</p><p>C++ 中的輸出指令是 <<,輸出的標的如果是螢幕,就用 cout。而 C++ 中的輸入指令則是>>,輸入的來源如果是鍵盤,就用 cin (代表 console input,唸成 see-in)。因此,在C++中要輸入資料可以寫成以下陳述式:</p><p>cin >> 變數;</p><p>這個陳述式所代表的動作為「從鍵盤輸入資料並存入指定的變數中」。在執行這個指令之前,我們要先定義一個變數來接收從鍵盤所接收到的資料。定義變數的語法如下:</p><p>資料型態 變數名稱;</p><p>比如說,如果你要定義一個名稱為 y 的整數變數,你可以寫成:</p><p>int y;</p><p>其中的 int 為型態名稱,y 則是所定義的變數名稱。當我們在程式中定義一個 int 變數時,VC++ 會為這個變數保留 4 個 Byte來它儲存這個數字。C++ 定義了許多不同的資料型態,int 是其中的一種,這個型態的變數可以儲存一個介於 (含) -2147483648 ~ 2147483647 之間的整數。</p><p>其實除了 int 以外,C++還定義了很多其它的「基礎資料型態」。坊間的電腦書多是在第一章或第二章就開始介紹各種資料型態,但是沒寫過程式的初學者很難了解這些概念。因此,我們把「基礎資料型態」留到第五章再仔細地討論,第四章以前所有的題目都只需要 int 變數就夠了。</p><p>在數學上,我們習慣使用一個字母的變數名稱,但是在寫程式時,我們卻希望變數名稱可以望文生義、不解自明。例如上面的例子我們定義了一個名稱為 y的變數,看程式的人大概很難從它的名稱去猜到這個變數所代表的意義。但是如果你把變數名稱定義為year,那麼每個人都可以知道這個變數就是用來儲存年份的。</p><p>C++允許你用較長的變數名稱來增加程式的可讀性,但是變數名稱的訂定也有它的規則:p </p><li>變數名稱僅能使用大小寫英文字母「A~Z」,「a~z」,阿拉伯數字「0~9」,以及底線「_」。底線在鍵盤上位於「-」的上面,要搭配「shift」來使用)。其他的符號 (包含空白) 都不可以出現在變數名稱之中。</li><li>變數名稱不能以阿拉伯數字「0~9」為開頭的第一個字母。雖然變數名稱可以用底線「_」開頭,可是由於系統變數多是以「_」作為第一個字母,我們應讓避免也使用這樣的變數名稱,以免混淆。</li><li>大小寫不同時視為不同的變數。不過習慣上我們通常會用小寫字母作為變數名稱,大寫字母來作為常數名稱。</li><li>不能用「保留字」作為變數名稱。所謂的「保留字」是系統保留下來作為特殊用途的字,不能再拿來作為變數名稱。目前我們的稱程式中所用到的「using」、「namespace」、「int」等就是保留字。VC++會把程式中的保留字改成藍色字體顯示,這個功能可以幫助我們辨識我們的變數名稱是否用到了保留字。</li><p>下表則是一些合法及不合法的變數名稱:</p><p>待補</p><p>不過由於本課程是以解題為主軸,而這些題目上如果已經定義了一些變數的名稱,我們倒是不妨予以沿用。一來這些變數名稱在題目中已經定義好了,會去看你的程式的人通常也都已經先看過題目了,所這些變數名稱不是沒有意義的。二來這些變數名稱比較短,可以讓你的程式看起來「簡潔」些。這題的「輸入說明」中已經把輸入的整數定義為y,代表一個西元年份,所以我們的程式中的變數不妨也沿用這個名稱。</p><p>有些解題的程式師喜歡把程式寫得很短,有的線上裁判甚至會公布你的程式碼的長度,於是就有人想盡辦法來縮短程式碼,以炫耀自己的功力。以軟體工程的角度來看,這是很要不得的,因為程式的可讀性遠比它的長度來得重要。筆者還是鼓勵大家用有意義的字來作為變數名稱。就算要使用較短的變數名稱,好歹也要用有意義的文字的縮寫。以下面的等加速度物體的位移公式為例:</p><p>d = v0t + 1/2 at2</p><p>其中的變數名稱雖然都只有一個字母,但卻都是有意義的:</p><p>d: displacement (位移)</p><p>v0: velocity at time 0 (時間 0 時的速度,初速度)</p><p>t: time (時間)</p><p>a: acceleration (加速度)</p><p>定義好了變數,也用 >> 從 cin 輸入了一個值並存到變數 y 裡,接下來我們就可以用 y來做運算,並把結果顯示出來,所得的程式如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-01.cpp"></script><p>電腦在執行你的程式時,是按照順序一行一行往下執行的,所以上面這個程式的三個陳述式出現的順序不可以隨意調換。一定要先用 int y;定義一個變數 y,接下來才會有一個變數 y 可以讓 cin >> y;來輸入資料;一定要先輸入一個西元年份 y,才能根據 y來計算民國年份。</p><p>程式寫好了,按 F5 開始執行。這時候出現了一個黑底的 DOS 視窗,但視窗上卻沒有任何的顯示。這時候請按一下工具列的</p><img style="width: 27px; height: 27px;" alt="alt" src="picture/ch02-01.PNG"/><img style="width: 200px; height: 170px;" alt="alt" src="picture/ch02-02.PNG"/><p>通常使用者在執行程式時,如果看到一個黑色的畫面卻沒有任何的顯示,由於他們不知道程式正在等他們輸入,所以他們很可能會以為程式當掉了。為了避免這樣的誤會,一般的程式設計書籍會建議在讓使用者輸入資料前,先輸出一些提示,如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-02.cpp"></script><p>如此一來,程式執行時就會先顯示「Please enter a year:」,使用者看到時便會知道要輸入一個年份(如果他英文不是太爛的話)。不過這樣的做法在解題時卻反而會造成一些問題,因為我們的程式不是要給一般的使用者來執行的,而是要上傳給線上裁判來執行。線上裁判不需要任何提示,只要程式一開始執行,它就會自動依題目中「輸入說明」的規定開始輸入資料。而程式中輸出到 cout的任何資料卻都會被視為你的答案的一部份,甚至包括一開始的「Please enter a year:」。由於你的答案比預存在伺服器上的標準答案多出了一些文字,比對的結果當然也就不一樣,你的程式也會因此而得到WA。因此,在寫解題用的程式時,除了在「輸出說明」所要求的輸出以外,千萬不要再畫蛇添足地加一些文字,因為那會影響答案的判斷。</p><p>現在你知道如何輸入一個值到變數裡,然後再利用那個變數去計算題目所要求的結果了。現在讓我們來看看這一題:</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d461">d461. 班際籃球賽</a></li><p>這個題目看起來很複雜,其實它非常簡單。不管賽程如何安排,每一場比賽都會淘汰一個隊伍,所以如果有 n 個隊伍,那麼就要舉行 n - 1場比賽才能產生最後的冠軍。簡單嗎?趕快去增加你的 AC 題數吧!</p><p>再練習一題:</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d063">d063. 0 與 1</a></li><p>題目大意:輸入的值只有兩種可能:0 與 1,請你輸出與輸入「相反」的數字,也就是輸入如果是 0,請你輸出 1;輸入如果是 1,請你輸出 0。</p><p>假設所輸入的值儲存於變數 x 中,你可以利用之前所教的「算術運算子」算出與 x「相反」的值嗎?</p><p>這題可以有很多種不同的寫法,你不妨先拿出一張紙來,不要看下面的答案,自己試著想一想,看看你的方法和筆者一不一樣。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-03.cpp"></script><p>直接用 1 去減掉 x 就好了。</p><h3>2.1.1 整數除法</h3><p>在 C++ 所定義的 5 個「算術運算子」中,「/」(除) 是要比較小心的一個,因為它所執行的是「整數除法」。如果你執行:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-04.cpp"></script><p>所顯示的結果不會是 2.75,而是 2。</p><p>筆者戲稱它為「小三除法」(是「小三」,請你不要倒過來唸!),因為小學三年級的小朋友沒有學過小數,如果你要他算 11 除以4,他只會算到整數位,然後告訴你「2餘3」。也就是說,他會給你兩個值,一個是「商」、一個是「餘數」。但是在程式語言中,一個運算子只能回傳一個值,於是 C++ 便分別為「商」和「餘數」各定義了一個運算子:「/」傳回商,「%」傳回餘數。因此,p </p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-04.cpp"></script><p>會顯示 2,而</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-05.cpp"></script><p>會顯示 3。</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d063">d063. 0 與 1</a></li><p>在上一節中,我們用減法很簡單地就解決了問題。但是那並不是唯一的解法,你也可以用餘數來解這一題:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-06.cpp"></script><p>先加 1 再除以 2 求餘數。如果 x 是 0,加 1 後就變成 1 了,但是如果 x 是 1,加 1 以後雖然變成了 2,但是再除以 2求餘數就變成 0 了。</p><p>那如果你同時需要商及餘數時怎麼辦?這時候你只能用「/」及「%」各算一次了。下面這題便是一個例子:</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d827">d827. 買鉛筆</a></li><p>題目大意:一支鉛筆 5 元,一打鉛筆 50 元。n 支鉛筆多少錢?</p><p>你可以用「/」求出要買幾打、用「%」求出要零買幾支,再依價錢算出總價即可。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-07.cpp"></script><p>2.1.2 無條件進位</p><p>由於「/」只算到整數位,之後的便捨去不再計算,因此,這樣的商可以視為是「無條件捨去」之後的結果。可是如果要「無條件進位」時怎麼辦?</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d073">d073. 分組報告</a></li><p>題目大意:從1號開始,每3個人分成一組。給你某人的編號n,請問他編在第幾組?</p><p>既然是每三個人一組,當然會用到 / 3 的運算。但是如果你直接寫:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-08.cpp"></script><p>這個寫法只有在n是3的倍數時才會有正確的結果,其他的情況所求出的組別會比實際的組別少 1。比如說,1 號和 2 號除以 3 會得到 0。</p><p>從另一個角度來看,「/」是無條件捨去,但是這題是要無條件進位。在執行整數的除法運算時,如果要無條件進位,只要在除以前先加上比除數還小 1的值即可。比如說,這題的除數是 3,那麼就在除法運算之前先加上 2 就可以了:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-09.cpp"></script><p>接下來再給你兩個練習題。在做這兩題時,提醒你要善用 % (餘數)運算子。</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d050">d050. 妳那裡現在幾點了?</a></li><li><a href="https://zerojudge.tw/ShowProblem?problemid=d060">d060. 還要等多久啊?</a></li></div><div class="para" id="para2" style="display:none;"><h2>2.2 多個變數的輸入</h2><p>我們再看一題:</p><li><a href="https://zerojudge.tw/ShowProblem?problemid=d485">d485.我愛偶數</a></li><p>這個題目要求我們計算 a 與 b 之間一共有幾個偶數。在計算之前,我們必須先定義並輸入 a 及 b 這兩個變數。首先,定義兩個變數你可以這麼寫:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-10.cpp"></script><p>但是如果你要定義的兩個變數的型態是相同的,(在這個兩個變數都是 int 型態),你可以把它們寫在同一個陳述式裡:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-11.cpp"></script><p>兩個變數之間要以一個逗號隔開來。 </p><p>接下來要輸入這兩個變數的值,我們可以這樣寫:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-12.cpp"></script><p>題目中的「輸入說明」部份提到所輸入的這兩個變數會用空白隔開來。其實 >> 可以讀入以任何「白空白」(white space)隔開的數字,也就是你在輸入這兩個數字時,把這兩個數字打在同一行,用空白隔開來;或是兩個數字各自打在一行都沒有關係,因為「空白」與「換行」都是「白空白」。</p><p>如果兩個數字是用白空白以外的字元隔開的話,那就比較麻煩了。如果我們輸入「1,2」,第一個陳述式 cin >> a; 會把 1 讀到a 裡,第二個陳述式 cin >> b; 卻讀到了「,2」,C++ 並沒有辦法正確地把 「,2」轉成整數,所以 b就沒有辦法得到正確的值了。還好,絕大部份的題目的輸入資料都是用空白來隔開數字,所以不用太擔心這個問題。</p><p>當我們有兩個、或兩個以上的變數需要輸入時,其實也可以把它寫成一行,(不同型態也沒有關係):</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-13.cpp"></script><h3>進階閱讀</h3><table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2"><tbody><tr><td style="vertical-align: top;"><p>為什麼可以寫成這樣呢?這個你得耐心聽我講。</p><p>之前當我們講到 >> 時,我們說它是「輸入指令」,<< 則是「輸出指令」。其實用更確切的方式來說,它們是運算子:>> 是「輸入 (擷取) 運算子」(Extraction Operator);<< 是「輸出 (插入)運算子」(Insertion Operator) 。查閱附錄B的運算子優先順序表,你會發現它們的優先順序為6,結合性則是由左至右。這種運算子的運作方式顛覆了你對運算子的既有印象。</p><p>傳統的算術運算子在執行過後會傳回一些我們所要的資訊。比如說 + 運算子會傳回兩數的和,- 運算子會傳回兩數的差......等。如果你執行123 + 456 的運算,它就會產生 579 的結果回傳給你。但是實在看不出來陳述式 cin >> a; 中 >>這個運算子倒底做了什麼「運算」?的確,並不是每 C++中的運算子都會透過計算產生一些計算來產生新的資訊回傳給你,在使用這些運算子時,它們的「副作用」(Side Effect)要遠比它們的回傳值還來得重要。</p><p>所謂的「副作用」就是在執行運算時對程式中的變數或執行環境產生了一些改變。以陳述式 cin >> a;為例,它的副作用就是從鍵盤輸入一個整數,並存入變數 a 之中。這個運算執行完畢時,a 的值就會被改變,這就是 >>運算子的「副作用」。</p><p>相對地,算術運算子卻是完全沒有「副作用」運算子。當你執行 a + b 的運算時,它會產生 a 與 b 的和並回傳給你,但是運算執行完後,變數a 與 b 本身的值並沒有任何改變。</p><p>就像算術運算子會有回傳的值一樣,其實每一個運算子都會有一個回傳的值。可是 >> 和 << 運算子倒底會回傳什麼東西呢?</p><p>>> 和 << 運算子在「運算」完畢之後,會回傳該運算子左側的「物件」。以陳述式 cin >> a;為例,>> 左側的物件就是 cin 本身,所以它回傳的「值」也是 cin。C++語言會自動忽略每個陳述式最後所回傳的值,所以執行陳述式 cin >> a; 之後所回傳的 cin 也被 C++所忽略,所以我們平常並不太在意 >> 運算子回傳的「值」是什麼。</p><p>但是當你在一個陳述式中使用好幾個 >> 運算子時,它的回傳值就很值得玩味了。</p><p>在陳述式 cin >> a >> b; 中,我們用了兩個 >> 運算子。因為 >>運算子的結合性為由左至右,因此左邊的那個 >> 運算子會先「算」,算完以後會回傳 cin,那麼下一個 >>運算子的運算就變成 cin >> b; 了。</p><p>為了讓你有個更清楚的概念,我們先用算術運算子的例子來讓你了解「回傳值」的應用。以下為運算式 1 + 2 * 3 的求值過程:</p><p>1 + 2 * 3 因為 * 的優先順序為 4,+ 的優先順序為 5,所以 * 先算。</p><p>1 + (2 * 3) 2 * 3 的回傳值回 6,所以用 6 來取代 (2 * 3)。</p><p>1 + 6 1 + 6 的回傳值為 7,所以用 7 來取代 1 + 6。</p><p>7 最後的回傳值為 7。</p><p>接下來我們來看看運算式 cin >> a >> b 的求值過程。</p><p>cin >> a >> b >> 的結合性為由左至右,所以左邊的>> 先算。</p><p>(cin >> a) >> b cin >> a 的回傳值為 cin(副作用:輸入 a 值),所以用 cin 來取代 (cin >> a)</p><p>cin >> b cin >> b 的回傳值為 cin (副作用:輸入 b值),所以用 cin 來取代 cin >> b</p><p>cin 最後的回傳值為 cin。</p><p>因為 C++ 會自動忽略陳述式的最後一個回傳值,所以陳述式 cin >> a >> b; 最後的回傳值 cin被忽略掉了。但是這類運算子重要的是它們的「副作用」,雖然最後的回傳值被忽略了,但是在求值的過程中,變數 a 和 b 的值卻都已經輸入完畢。</p><p>由於 C++會自動忽略最後一個回傳值,所以一個陳述式的最後一個執行的運算子必須是有「副作用」的,否則那個運算子就沒有意義了。比如說,下面這個陳述式是完全合法的,你也可以把它編譯後執行,但是這個陳述式卻沒有任何意義,因為它辛苦求得的最後回傳值 106638 被 C++ 給忽略了。</p><p> 123 + (456 - 321) * 789;</p><p>但是如果把它改成:</p><p> cout << 123 + (456 - 321) * 789;</p><p>這陳述式中最後一個執行的運算為 cout << 106638,其回傳值為 cout,雖然它被 C++ 給忽略了,但是運算式 123+ (456 - 321) * 789 的結果卻已經透過 << 的「副作用」顯示在螢幕上了。</p></td></tr></tbody></table><p>下面這個程式可以輸入兩個整數的值 a, b (b ≥ a),然後輸出它們的差:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-14.cpp"></script><p>但是「d485. 我愛偶數」這題不是要你求它們的差,而是要你求它們之間有幾個偶數。很難嗎?沒有關係,筆者給你一些提示:≥ a 的最小偶數為 a+ a%2;≤ b 的最大偶數為 b - b%2。這樣你會算了嗎?</p><p>小提醒:要注意運算子的順序,必要時要加括號。</p></div><div class="para" id="para3" style="display:none;"><h2>2.3 指定運算子</h2><li><a href="https://zerojudge.tw/ShowProblem?problemid=d489">d489. 伏林的三角地</a></li><p>給你三角形的三邊長,要你求出這個三角形的面積的平方。</p><p>講到三角形的面積,你一定會想到「海龍公式」。</p><p>T=sqrt{s(s-a)(s-b)(s-c)} sqrt:根號</p><p>其中 s=(a+b+c)除以2.</p><p>只是因為我們求的是面積的平方,所以「海龍公式」最後一個開平方的動作就可以不用作了。可是要把這個公式寫成程式時,卻產生了一個問題:這個公式是二段式的,我們得先求出 s (週長的一半),再把 s 代入公式的第二段。可是到目前為止,我們所使用的 cout <<輸出方式好像只能一次就把答案求出來,不能先產 s 這個中間產物。當然,我們也可以把 s 直接代入第二段寫成以下陳述式:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-15.cpp"></script><p>我們也可以用數學的觀念將這個陳述式簡化為:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-16.cpp"></script><p>但是它還是比原來的海龍公式還要長且複雜,電腦要花較長的時間來計算這個公式,而且沒有幾個人看得出來這是海龍公式。</p><p>要達到海龍公式原始的二段式模式,我們需要用到「指定運算子」(Assignment Operator),也就是 =(等號)。和大多數的算術運算子一樣,「=」是一個二元運算子,也就是說,等號的前後都需要有一個運算元。其語法為:</p><p>v = Ep </p><li>v:變數</li><li>E:運算式</li><p>在等號的左邊必須擺一個變數,把 = 右邊的值指定給 = 左邊的變數。</p><p>和 << 及 >> 運算子一樣,這個運算子的「副作用」比它的回傳值還重要。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-17.cpp"></script><p>參考一下附錄 B 的運算子優先順序,/ 的優先順序為 4,= 的優先順序為 15,所以 / 會先算,所得的回傳值會由 =運算子指定給s作為它的值。當這個陳述式執行完畢時,螢幕上並不會有任何的顯示,但是 s 會得到一個新的值,這就是 = 運算子的「副作用」。</p><p>一旦 s 變數內已經有了週長的一半,接下來我們就可以把它代入第二階段的海龍公式並把結果輸出了。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-18.cpp"></script><p>題目中有說明三邊長 a, b, c 均為整數,即使如此,所求得的 s 卻可能有小數。不過題目也說明了,答案一定是整數,可是如果 s 有小數且a, b, c 均是整數,答案就不可能是整數。因此,題目中所給的資料所求出的 s 也不可能有小數,你可以放心地把 s 定義為整數變數。</p></div><div class="para" id="para4" style="display:none;"><h2>2.4 複合指定運算子</h2><h3>複合指定運算子</h3><li><a href="https://zerojudge.tw/ShowProblem?problemid=d490">d490. 我也愛偶數</a></li><p>要計算一個整數區間 (a 與 b 之間) 的偶數和,我們可以利用梯形公式。如果 a 與 b 本身都是偶數,這題就簡單了:</p><li>上底 → a</li><li>下底 → b</li><li>高 → (b - a) / 2 + 1</li><li>面積 → (a + b) * ((b - a) / 2 + 1) / 2</li><p>但是因為 a 和 b 不一定是偶數,所以我們得額外再做些處理。之前我們在解「d485.我愛偶數」時,用的就是上面「高」的公式。在「高」的公式中,a 和 b 都只出現一次,我們可以直接用 a + a%2 來取代公式中的 a、用 b- b%2 取代公式中的 b 就行了。但是這次我們用的是「面積」的公式,其中 a 和 b 各出現 2次,如果我們還是直接代進去,一來公式變很長,二來額外的運算也會浪費 CPU 的時間。</p><p>即然我們學過了 = 運算子,我們可以用之前的二段式處理:</p><p>a' = a + a%2</p><p>b' = b - b%2</p><p>然後再用 a' 及 b' 去代上面的梯形面積公式。</p><p>但是之前我們說過,變數名稱中只能有英文字母、數字、及底線。因此 a' 及 b' 並不是合法的 C++ 變數名稱。如果你要的話,可以用 a1 及b1 來代替 a' 及 b' 。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-19.cpp"></script><p>你只要把上面的這段程式套入程式的「殼」裡,並定義及輸入所需要的變數,這題就可以 AC了。不過筆者在這裡要更進一步地討論指定運算字的一些特性與運用。</p><p>我們先仔細看一下 a1 = a + a%2; 這個陳述式。在這個陳述式中一共有 =, +, 及 %三個運算子,根據運算子的優先順序,它們執行的順序為先算 %,再算 +,最後再算 =。</p><p>假設 a 等於 5,那麼 a + a%2 就會等於 6,仔細觀察一下上面的程式,當電腦算完這個部份的運算之後,a變數的值就再也沒有用到了,所以 a 內容也沒有繼續保留的必要。這時候程式大可以把所求得的 6 直接回存到 a 變數裡,而不需要再另外定義一個a1 變數。於是這個陳述式就變成了:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-20.cpp"></script><p>這樣的寫法對很多初學者而言是難以接受的,因為他們會把它和數學上的表示法互相混淆。以數學的角度來看上面的式子,你會說 a一定是偶數,否則它不成立。但是這個 = 運算子並不是數學上用在等式中的那個等號,而是用來指定一個值給它左側的變數。我們來看另一個更極端的式子:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-21.cpp"></script><p>從數學的角度來看,這個式子根本就是無解。可是在 C++ 裡,這個陳述式卻代表把 a 變數的值加 1。如果 a 原來是 5,執行後就會變6;如果 a 原來是 6,執行後就會變 7。</p><p>這樣的運算式在電腦中很常見,我們通常稱之為「累加」。除了「累加」以外,如果搭配 *運算子的話,就成了「累乘」了,而這類的運算我們就統稱為「累算」。由於這類的運算很常見,所以 C++就提供了更簡潔、更有效率的「複合指定運算子」(Compound Assignment Operators)。</p><p>a = a + 1; 可以寫成 a += 1;</p><p>a = a + a%2; 可以寫成 a += a%2;</p><p>a = a * 10; 可以寫成 a *= 10;</p><p>以此類推</p><p>你可以在附錄 B 的運算子優先順序表中找到所謂的「複合指定運算子」。不過使用時要注意,和 = 運算字結合的運算子必須是最後一個執行的運算,例如:</p><p>a = a * 10 + 1; 就不可以寫成 a *= 10 + 1; </p><p>因為 a *= 10 + 1; 的效果其實等於 a = a * (10 + 1);</p><p>如果 a 原來等於 5,執行 a = a * 10 + 1; 之後 a 會等於 51;可是執行 a *= 10 + 1; 之後 a 卻變成55 了。</p><p>整合以上的討論,本題的程式如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-22.cpp"></script><h3>註解</h3><p>把一個較複雜的程式分成幾段來寫也比較容易讓人了解。分段的方式也方便我們為程式加上「註解」。在 C++ 裡註解是以 // (兩個斜線)開始,至該行的結尾結束。以「d490. 我也愛偶數」的程式為例,我們為它加上註解如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-23.cpp"></script><p>當程式在編譯時,所有介於 //與行尾之間的文字都會被編譯器所忽略,因此它不會對程式的執行產生任何影響。註解的主要目的是為了讓程式比較容易看懂。很多程式師懶得為自己的程式寫註解,結果幾年後連他自己都看不懂自己的程式,因此程式師們應該養成寫註解的良好習慣。</p><h3>進階閱讀</h3><table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2"><tbody><tr><td style="vertical-align: top;"><p>C++ 有兩種註解的方式:「單行註解」及「多行註解」。上面所介紹的是單行註解,這種註解的方式是 C++ 才有的新方式,C語言只有多行註解可用。多行註解以 /* 作為開始,以 */ 作為結束,中間可以打很多行的註解。上面的程式改成多行的註解方式如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-24.cpp"></script><p>多行註解有很多的問題。首先,如果第 8 行的結尾你忘了打 */ (如下),那麼第 9 行整行都會被視為註解,其中的 b -= b % 2;陳述式也不會執行了。</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-25.cpp"></script><p>像這樣的錯誤並不會被編譯器偵測到,程式可以順利執行,但是結果卻不對,很難去抓錯。/p </p><p>其次,有時候我們會想要取消一部份程式碼的作用,但是又不想要把它刪掉,我們會把它們變成「註解」。這時候你可能會認為多行的註解比較好用,因為你要在要註解掉的程式碼的前後分別加上一個 /* 及 */ 就可以了,哪怕是幾百行的程式也是一次 OK!不過這是很危險的一件事。以上面這個程式為例,把第8 行及第 9 行註解掉以後程式如下:</p><script src="https://gist.github.com/allem40306/1e34e9ca39595c8b7729d52a25fabf1b.js?file=ch02-26.cpp"></script><p>要留意多行註解是不能套疊的,第 8 行的 /* 開始了註解,可是到了第 9 行的行尾看到了 */,以為註解已經結束了,所以便開始執行第 10行的程式。</p><p>因此我們不建議使用多行的註解,(可憐的 C 程式師只有多行註解可用)。</p><p>其實現的編譯器提供的很強大的編輯功能。以 C++ 為例,如果你要把一大段的程式碼註解掉,你只要把那段程式碼反白,再按工具列上的</p><img style="width: 25px; height: 25px;" alt="" src="picture/ch02-03.PNG"/><img style="width: 25px; height: 25px;" alt="" src="picture/ch02-04.PNG"/></td></tr></tbody></table></div></body>