日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

5.2 基本運算符

C用運算符(operator)表示算術運算。例如,+運算符使在它兩側的值加在一起。如果你覺得術語“運算符”很奇怪,那么請記住東西總得有個名稱。與其叫“那些東西”或“運算處理符”,還不如叫“運算符”。現在,我們介紹一下用于基本算術運算的運算符:=、+、-、*和/(C沒有指數運算符。不過,C的標準數學庫提供了一個pow()函數用于指數運算。例如,pow(3.5, 2.2)返回3.5的2.2次冪)。

5.2.1 賦值運算符:=

在C語言中,=并不意味著“相等”,而是一個賦值運算符。下面的賦值表達式語句:

bmw = 2002;

把值2002賦給變量bmw。也就是說,=號左側是一個變量名,右側是賦給該變量的值。符號=被稱為賦值運算符。另外,上面的語句不讀作“bmw等于2002”,而讀作“把值2002賦給變量bmw”。賦值行為從右往左進行。

也許變量名和變量值的區別看上去微乎其微,但是,考慮下面這條常用的語句:

i = i + 1;

對數學而言,這完全行不通。如果給一個有限的數加上1,它不可能“等于”原來的數。但是,在計算機賦值表達式語句中,這很合理。該語句的意思是:找出變量i的值,把該值加1,然后把新值賦值變量i(見圖5.1)。

C語言有大約40個運算符,最常用的有這些

 

圖5.1 語句i = i + 1;

在C語言中,類似這樣的語句沒有意義(實際上是無效的):

2002 = bmw;

因為在這種情況下,2002被稱為右值(rvalue),只能是字面常量,不能給常量賦值,常量本身就是它的值。因此,在編寫代碼時要記住,=號左側的項必須是一個變量名。實際上,賦值運算符左側必須引用一個存儲位置。最簡單的方法就是使用變量名。不過,后面章節還會介紹“指針”,可用于指向一個存儲位置。概括地說,C使用可修改的左值(modifiable lvalue)標記那些可賦值的實體。也許“可修改的左值”不太好懂,我們再來看一些定義。

幾個術語:數據對象、左值、右值和運算符

賦值表達式語句的目的是把值存儲到內存位置上。用于存儲值的數據存儲區域統稱為數據對象(data object)。C標準只有在提到這個概念時才會用到對象這個術語。使用變量名是標識對象的一種方法。除此之外,還有其他方法,但是要在后面的章節中才學到。例如,可以指定數組的元素、結構的成員,或者使用指針表達式(指針中存儲的是它所指向對象的地址)。左值(lvalue)是C語言的術語,用于標識特定數據對象的名稱或表達式。因此,對象指的是實際的數據存儲,而左值是用于標識或定位存儲位置的標簽。

對于早期的C語言,提到左值意味著:

1.它指定一個對象,可以引用內存中的地址;

2.它可用在賦值運算符的左側,左值(lvalue)中的l源自left。

但是后來,標準中新增了const限定符。用const創建的變量不可修改。因此,const標識符滿足上面的第1項,但是不滿足第2項。一方面C繼續把標識對象的表達式定義為左值,一方面某些左值卻不能放在賦值運算符的左側。

為此,C標準新增了一個術語:可修改的左值(modifiable lvalue),用于標識可修改的對象。所以,賦值運算符的左側應該是可修改的左值。當前標準建議,使用術語對象定位值(object locator value)更好。

右值(rvalue)指的是能賦值給可修改左值的量,且本身不是左值。例如,考慮下面的語句:

bmw = 2002;

這里,bmw是可修改的左值,2002是右值。讀者也許猜到了,右值中的r源自right。右值可以是常量、變量或其他可求值的表達式(如,函數調用)。實際上,當前標準在描述這一概念時使用的是表達式的值(value of an expression),而不是右值。

我們看幾個簡單的示例:

int ex;
int why;
int zee;
const int TWO = 2;
why = 42;
zee = why;
ex = TWO * (why + zee);

這里,ex、why和zee都是可修改的左值(或對象定位值),它們可用于賦值運算符的左側和右側。TWO是不可改變的左值,它只能用于賦值運算符的右側(在該例中,TWO被初始化為2,這里的=運算符表示初始化而不是賦值,因此并未違反規則)。同時,42是右值,它不能引用某指定內存位置。另外,why和zee是可修改的左值,表達式(why + zee)是右值,該表達式不能表示特定內存位置,而且也不能給它賦值。它只是程序計算的一個臨時值,在計算完畢后便會被丟棄。

在學習名稱時,被稱為“項”(如,賦值運算符左側的項)的就是運算對象(operand)。運算對象是運算符操作的對象。例如,可以把“吃漢堡”描述為:“吃”(運算符)操作“漢堡”(運算對象)。類似地可以說,=運算符的左側運算對象應該是可修改的左值。

C的基本賦值運算符有些與眾不同,請看程序清單5.3。

程序清單5.3 golf.c程序

/* golf.c -- 高爾夫錦標賽記分卡 */
#include <stdio.h>
int main(void)
{
     int jane, tarzan, cheeta;

     cheeta = tarzan = jane = 68;
     printf("                  cheeta   tarzan    janen");
     printf("First round score %4d %8d %8dn", cheeta, tarzan, jane);

     return 0;
}

許多其他語言都會回避該程序中的三重賦值,但是C完全沒問題。賦值的順序是從右往左:首先把68賦給jane,然后再賦給tarzan,最后賦給cheeta。因此,程序的輸出如下:

                  cheeta    tarzan      jane
First round score   68         68         68

5.2.2 加法運算符:+

加法運算符(addition operator)用于加法運算,使其兩側的值相加。例如,語句:

printf("%d", 4 + 20);

打印的是24,而不是表達式

4 + 20

相加的值(運算對象)可以是變量,也可以是常量。因此,執行下面的語句:

income = salary + bribes;

計算機會查看加法運算符右側的兩個變量,把它們相加,然后把和賦給變量income。

在此提醒讀者注意,income、salary和bribes都是可修改的左值。因為每個變量都標識了一個可被賦值的數據對象。但是,表達式salary + bribes是一個右值。

5.2.3 減法運算符:-

減法運算符(subtraction operator)用于減法運算,使其左側的數減去右側的數。例如,下面的語句把200.0賦給takehome:

takehome = 224.00 – 24.00;

+和-運算符都被稱為二元運算符(binary operator),即這些運算符需要兩個運算對象才能完成操作。

5.2.4 符號運算符:-和+

減號還可用于標明或改變一個值的代數符號。例如,執行下面的語句后,smokey的值為12:

rocky = –12;
smokey = –rocky;

以這種方式使用的負號被稱為一元運算符(unary operator)。一元運算符只需要一個運算對象(見圖5.2)。

C語言有大約40個運算符,最常用的有這些

 

圖5.2 一元和二元運算符

C90標準新增了一元+運算符,它不會改變運算對象的值或符號,只能這樣使用:

dozen = +12;

編譯器不會報錯。但是在以前,這樣做是不允許的。

5.2.5 乘法運算符:*

符號*表示乘法。下面的語句用2.54乘以inch,并將結果賦給cm:

cm = 2.54 * inch;

C沒有平方函數,如果要打印一個平方表,怎么辦?如程序清單5.4所示,可以使用乘法來計算平方。

程序清單5.4 squares.c程序

/* squares.c -- 計算1~20的平方 */
#include <stdio.h>
int main(void)
{
     int num = 1;

     while (num < 21)
     {
          printf("%4d %6dn", num, num * num);
          num = num + 1;
     }

     return 0;
}

該程序打印數字1~20及其平方。接下來,我們再看一個更有趣的例子。

1.指數增長

讀者可能聽過這樣一個故事,一位強大的統治者想獎勵做出突出貢獻的學者。他問這位學者想要什么,學者指著棋盤說,在第1個方格里放1粒小麥、第2個方格里放2粒小麥、第3個方格里放4粒小麥,第4個方格里放8粒小麥,以此類推。這位統治者不熟悉數學,很驚訝學者竟然提出如此謙虛的要求。因為他原本準備獎勵給學者一大筆財產。如果程序清單5.5運行的結果正確,這顯然是跟統治者開了一個玩笑。程序計算出每個方格應放多少小麥,并計算了總數。可能大多數人對小麥的產量不熟悉,該程序以谷粒數為單位,把計算的小麥總數與粗略估計的世界小麥年產量進行了比較。

程序清單5.5 wheat.c程序

/* wheat.c -- 指數增長 */
#include <stdio.h>
#define SQUARES 64             // 棋盤中的方格數
int main(void)
{
     const double CROP = 2E16;  // 世界小麥年產谷粒數
     double current, total;
     int count = 1;

     printf("square     grains       total     ");
     printf("fraction of n");
     printf("           added        grains    ");
     printf("world totaln");
     total = current = 1.0;        /* 從1顆谷粒開始   */
     printf("%4d %13.2e %12.2e %12.2en", count, current,
               total, total / CROP);
     while (count < SQUARES)
     {
          count = count + 1;
          current = 2.0 * current;    /* 下一個方格谷粒翻倍 */
          total = total + current;    /* 更新總數 */
          printf("%4d %13.2e %12.2e %12.2en", count, current,
                    total, total / CROP);
     }
     printf("That's all.n");

     return 0;
}

程序的輸出結果如下:

square        grains       total        fraction of
              added        grains       world total
    1         1.00e+00     1.00e+00     5.00e-17
    2         2.00e+00     3.00e+00     1.50e-16
    3         4.00e+00     7.00e+00     3.50e-16
    4         8.00e+00     1.50e+01     7.50e-16
    5         1.60e+01     3.10e+01     1.55e-15
    6         3.20e+01     6.30e+01     3.15e-15
    7         6.40e+01     1.27e+02     6.35e-15
    8         1.28e+02     2.55e+02     1.27e-14
    9         2.56e+02     5.11e+02     2.55e-14
   10         5.12e+02     1.02e+03     5.12e-14

10個方格以后,該學者得到的小麥僅超過了1000粒。但是,看看55個方格的小麥數是多少:

55         1.80e+16     3.60e+16     1.80e+00

總量已超過了世界年產量!不妨自己動手運行該程序,看看第64個方格有多少小麥。

這個程序示例演示了指數增長的現象。世界人口增長和我們使用的能源都遵循相同的模式。

5.2.6 除法運算符:/

C使用符號/來表示除法。/左側的值是被除數,右側的值是除數。例如,下面four的值是4.0:

four = 12.0/3.0;

整數除法和浮點數除法不同。浮點數除法的結果是浮點數,而整數除法的結果是整數。整數是沒有小數部分的數。這使得5除以3很讓人頭痛,因為實際結果有小數部分。在C語言中,整數除法結果的小數部分被丟棄,這一過程被稱為截斷(truncation)。

運行程序清單5.6中的程序,看看截斷的情況,體會整數除法和浮點數除法的區別。

程序清單5.6 divide.c程序

/* divide.c -- 演示除法 */
#include <stdio.h>
int main(void)
{
     printf("integer division:  5/4   is %d n", 5 / 4);
     printf("integer division:  6/3   is %d n", 6 / 3);
     printf("integer division:  7/4   is %d n", 7 / 4);
     printf("floating division: 7./4. is %1.2f n", 7. / 4.);
     printf("mixed division:    7./4  is %1.2f n", 7. / 4);

     return 0;
}

程序清單5.6中包含一個“混合類型”的示例,即浮點值除以整型值。C相對其他一些語言而言,在類型管理上比較寬容。盡管如此,一般情況下還是要避免使用混合類型。該程序的輸出如下:

integer division:  5/4   is 1
integer division:  6/3   is 2
integer division:  7/4   is 1
floating division: 7./4. is 1.75
mixed division:    7./4  is 1.75

注意,整數除法會截斷計算結果的小數部分(丟棄整個小數部分),不會四舍五入結果。混合整數和浮點數計算的結果是浮點數。實際上,計算機不能真正用浮點數除以整數,編譯器會把兩個運算對象轉換成相同的類型。本例中,在進行除法運算前,整數會被轉換成浮點數。

C99標準以前,C語言給語言的實現者留有一些空間,讓他們來決定如何進行負數的整數除法。一種方法是,舍入過程采用小于或等于浮點數的最大整數。當然,對于3.8而言,處理后的3符合這一描述。但是-3.8會怎樣?該方法建議四舍五入為-4,因為-4小于-3.8。但是,另一種舍入方法是直接丟棄小數部分。這種方法被稱為“趨零截斷”,即把-3.8轉換成-3。在C99以前,不同的實現采用不同的方法。但是C99規定使用趨零截斷。所以,應把-3.8轉換成-3。

5.2.7 運算符優先級

考慮下面的代碼:

butter = 25.0 + 60.0 * n / SCALE;

這條語句中有加法、乘法和除法運算。先算哪一個?是25.0加上60.0,然后把計算的和85.0乘以n,再把結果除以SCALE?還是60.0乘以n,然后把計算的結果加上25.0,最后再把結果除以SCALE?還是其他運算順序?假設n是6.0,SCALE是2.0,帶入語句中計算會發現,第1種順序得到的結果是255,第2種順序得到的結果是192.5。C程序一定是采用了其他的運算順序,因為程序運行該語句后,butter的值是205.0。

顯然,執行各種操作的順序很重要。C語言對此有明確的規定,通過運算符優先級來解決操作順序的問題。每個運算符都有自己的優先級。正如普通的算術運算那樣,乘法和除法的優先級比加法和減法高,所以先執行乘法和除法。如果兩個運算符的優先級相同怎么辦?如果它們處理同一個運算對象,則根據它們在語句中出現的順序來執行。對大多數運算符而言,這種情況都是按從左到右的順序進行(=運算符除外)。因此,語句:

butter = 25.0 + 60.0 * n / SCALE;

的運算順序是:

60.0 * n            首先計算表達式中的*或/(假設n的值是6,所以60.0*n得360.0)
360.0 / SCALE       然后計算表達式中第2個*或/
25.0 + 180          最后計算表達式里第1個+或-,結果為205.0(假設SCALE的值是2.0)

許多人喜歡用表達式樹(expression tree)來表示求值的順序,如圖5.3所示。該圖演示了如何從最初的表達式逐步簡化為一個值。

C語言有大約40個運算符,最常用的有這些

 

圖5.3 用表達式樹演示運算符、運算對象和求值順序

如何讓加法運算在除法運算之前執行?可以這樣做:

flour = (25.0 + 60.0 * n) / SCALE;

最先執行圓括號中的部分。圓括號內部按正常的規則執行。該例中,先執行乘法運算,再執行加法運算。執行完圓括號內的表達式后,用運算結果除以SCALE。

表5.1總結了到目前為止學過的運算符優先級。

表5.1 運算符優先級(從高至低)

C語言有大約40個運算符,最常用的有這些

 

注意正號(加號)和負號(減號)的兩種不同用法。結合律欄列出了運算符如何與運算對象結合。例如,一元負號與它右側的量相結合,在除法中用除號左側的運算對象除以右側的運算對象。

5.2.8 優先級和求值順序

運算符優先級為表達式中的求值順序提供重要的依據,但是并沒有規定所有的順序。C給語言的實現者留出選擇的余地。考慮下面的語句:

y = 6 * 12 + 5 * 20;

當運算符共享一個運算對象時,優先級決定了求值順序。例如上面的語句中,12是*和+運算符的運算對象。根據運算符的優先級,乘法的優先級比加法高,所以先進行乘法運算。類似地,先對5進行乘法運算而不是加法運算。簡而言之,先進行兩個乘法運算6 * 12和5 * 20,再進行加法運算。但是,優先級并未規定到底先進行哪一個乘法。C語言把主動權留給語言的實現者,根據不同的硬件來決定先計算前者還是后者。可能在一種硬件上采用某種方案效率更高,而在另一種硬件上采用另一種方案效率更高。無論采用哪種方案,表達式都會簡化為72 + 100,所以這并不影響最終的結果。但是,讀者可能會根據乘法從左往右的結合律,認為應該先執行+運算符左邊的乘法。結合律只適用于共享同一運算對象的運算符。例如,在表達式12 / 3 * 2中,/和*運算符的優先級相同,共享運算對象3。因此,從左往右的結合律在這種情況起作用。表達式簡化為4 * 2,即8(如果從右往左計算,會得到12/6,即2,這種情況下計算的先后順序會影響最終的計算結果)。在該例中,兩個*運算符并沒有共享同一個運算對象,因此從左往右的結合律不適用于這種情況。

學以致用

接下來,我們在更復雜的示例中使用以上規則,請看程序清單5.7。

程序清單5.7 rules.c程序

/* rules.c -- 優先級測試 */
#include <stdio.h>
int main(void)
{
     int top, score;

     top = score = -(2 + 5) * 6 + (4 + 3 * (2 + 3));
     printf("top = %d, score = %dn", top, score);

     return 0;
}

該程序會打印什么值?先根據代碼推測一下,再運行程序或閱讀下面的分析來檢查你的答案。

首先,圓括號的優先級最高。先計算-(2 + 5) * 6中的圓括號部分,還是先計算(4 + 3 * (2 + 3))中的圓括號部分取決于具體的實現。圓括號的最高優先級意味著,在子表達式-(2 + 5) * 6中,先計算(2 + 5)的值,得7。然后,把一元負號應用在7上,得-7。現在,表達式是:

top = score = -7 * 6 + (4 + 3 * (2 + 3))

下一步,計算2 + 3的值。表達式變成:

top = score = -7 * 6 + (4 + 3 * 5)

接下來,因為圓括號中的*比+優先級高,所以表達式變成:

top = score = -7 * 6 + (4 + 15)

然后,表達式為:

top = score = -7 * 6 + 19

-7乘以6后,得到下面的表達式:

top = score = -42 + 19

然后進行加法運算,得到:

top = score = -23

現在,-23被賦值給score,最終top的值也是-23。記住,=運算符的結合律是從右往左。

本文摘自《C Primer Plus(第6版)中文版》

分享到:
標簽:語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定