C語言基礎學習day04
運算子、表示式和語句
迴圈簡介
#include <stdio.h> #define ADJUST 7.31 int main(void) { const double SCALE=0.333; double shoe,foot; printf("Shoe size (men's) foot length\n"); shoe=3.0; while(shoe<18.5) { foot=SCALE*shoe+ADJUST; printf("%10.1f %15.2f inches\n",shoe,foot); shoe=shoe+1.0; } printf("if the shoe fits,wear it.\n"); return 0; }
當程式第一次到達while迴圈時,會檢查圓括號中的條件是否為真,條件為真,程式進入塊中繼續執行,把尺碼轉換成英寸
然後列印計算的 結果。下一條語句把 shoe增加1.0,使shoe的值為4.0:
shoe = shoe + 1.0;
此時,程式返回while入口部分檢查條件。
為何要返回while的入口部分?因為上面這條語句的下面是右花括號(}),程式碼使用一對花括號 ({})來標出while迴圈的範圍。花括號之間的內容就是要被重複執行的內 容。花括號以及被花括號括起來的部分被稱為塊(block)。現在,回到程式中。因為4小於18.5,所以要重複執行被花括號括起來的所有內容(用計算機術語來說就是,程式迴圈這些語句)。該迴圈過程一直持續到shoe的值為19.0。此時,由於19.0小於18.5,所以該條件為假:
shoe < 18.5
出現這種情況後,控制轉到緊跟while迴圈後面的第1條語句。該例中, 是最後的printf()語句。
基本運算子
賦值運算子:=
在編寫程式碼時要記住,=號左側的項必須是一個變數名。實際上,賦值運算子左側必須引用一個儲存位置。最簡單的方法就是使用變數名。不過,後面章節還會介紹“指標”,可用於指向一個儲存位置。概括地說,C 使用可修改的左值(modifiable lvalue) 標記那些可賦值的實體。
“項”(如,賦值運算子左側的項)的就是運算物件(operand)。運算物件是運算子操作的物件。例如,可以把吃漢堡描述 為:“吃”運算子操作“漢堡”運算物件。類似地可以說,=運算子的左側運算物件應該是可修改的左值
#include<stdio.h> intmain(void) { int jane,tarzan,cheeta; cheeta =tarzan =jane =68; printf(" cheeta tarzan jane\n"); printf("first round score %4d %8d %8d\n",cheeta,tarzan,jane); return 0; }
許多其他語言都會迴避該程式中的三重賦值,但是C完全沒問題。賦值 的順序是從右往左:首先把86賦給jane,然後再賦給tarzan,最後賦給 cheeta
執行結果:
cheeta tarzan jane
first round score 68 68 68
加法運算子:+
加法運算子(addition operator)用於加法運算,使其兩側的值相加。例如,語句:
printf("%d", 4 + 20);
列印的是24,而不是表示式
4 + 20
相加的值(運算物件)可以是變數,也可以是常量。
乘法運算子:*
該程式列印數字1~20及其平方
#include<stdio.h> int main(void) { int num=1; while (num <21) { printf("%4d%6d\n",num,num*num); num=num+1; } return 0; }
#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 grain "); printf("world total\n"); total=current=1.0; printf("%4d %13.2e %12.2e %12.2e\n", 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.2e\n",count,current,total,total/CROP); } printf("That's all.\n"); return 0; }
當遇到error: stray '\241' in program錯誤的解決方法:該錯誤是指源程式中有非法字元,需要將非法字元去掉。一般是由於coder使用中文輸入法或者從別的地方直接複製貼上程式碼造成的。程式碼中出現了中文空格,中文引號, 各種中文標點符號都會出現,簡單修改一下就OK了
除法運算子:/
#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; }
執行結果:
注意,整數除法會截斷計算結果的小數部分(丟棄整個小數部分),不 會四捨五入結果。混合整數和浮點數計算的結果是浮點數。
實際上,計算機不能真正用浮點數除以整數,編譯器會把兩個運算物件轉換成相同的型別。 本例中,在進行除法運算前,整數會被轉換成浮點數。
C99標準以前,C語言給語言的實現者留有一些空間,讓他們來決定如何進行負數的整數除法。一種方法是,舍入過程採用小於或等於浮點數的最大整數。當然,對於3.8而言,處理後的3符合這一描述。但是-3.8 會怎樣? 該方法建議四捨五入為-4,因為-4 小
於-3.8.但是,另一種舍入方法是直接丟 棄小數部分。這種方法被稱為“趨零截斷”,即把-3.8轉換成-3。在C99以前, 不同的實現採用不同的方法。但是C99規定使用趨零截斷。所以,應把-3.8 轉換成-3
運算子優先順序
#include<stdio.h> int main(void) { int top,score; top=score=-(2+5)*6+(4+3*(2+3)); printf("top = %d,score = %d\n",top,score); return 0; }
輸出結果:-23
sizeof運算子和size_t型別
sizeof運算子以位元組為單 位返回運算物件的大小(在C中,1位元組定義為char型別佔用的空間大小。過去,1位元組通常是8位,但是一些字符集可能使用更大的位元組
#include<stdio.h> int main(void) { int n = 0; size_t intsize; intsize = sizeof(int); printf("n = %d, n has %zd bytes; all ints have %zd bytes.\n",n,sizeof n,intsize); return 0; }
執行結果:
n = 0, n has 4 bytes; all ints have 4 bytes.
C 語言規定,sizeof 返回 size_t 型別的值。這是一個無符號整數型別, 但它不是新型別,size_t是語言定義的標準型別
C有一個 typedef機制,允許程式設計師為現有型別建立別名。例如,
typedef double real;
這樣,real就是double的別名。現在,可以宣告一個real型別的變數:
real deal; // 使用typedef
編譯器檢視real時會發現,在typedef宣告中real已成為double的別名,於是把deal建立為double 型別的變數。類似地,C 標頭檔案系統可以使用 typedef 把 size_t 作為 unsigned int 或unsigned long的別名。這樣,在使用size_t型別 時,編譯器會根據不同的系統替換標準型別
求模運算子:%
負數求模如何進行?C99規定“趨零截斷”之前,該問題的處理方法很 多。但自從有了這條規則之後,如果第1個運算物件是負數,那麼求模的結 果為負數;如果第1個運算物件是正數,那麼求模的結果也是正數:
11 / 5得2,11 % 5得1
11 / -5得-2,11 % -2得1
-11 / -5得2,-11 % -5得-1
-11 / 5得-2,-11 % 5得-1
如果當前系統不支援C99標準,會顯示不同的結果。實際上,標準規 定:無論何種情況,只要a和b都是整數值,便可通過a - (a/b)*b來計算a%b。
例如,可以這樣計算-11%5:
-11 - (-11/5) * 5 = -11 -(-2)*5 = -11 -(-10) = -1
遞增運算子:++
該運算子以兩種方式出現:
第1種方式,++出現在其作用的變數前面, 這是字首模式;
第2種方式,++出現在其作用的變數後面,這是字尾模式。
#include<stdio.h> int main(void) { int a=1,b=1; int a_post,pre_b; a_post=a++;//字尾遞增 pre_b=++b;//字首遞增 printf("a a_post b pre_b \n"); printf("%1d %5d %5d %5d\n",a,a_post,b,pre_b); return 0; }
執行結果:
a和b都遞增了1,但是,a_post是a遞增之前的值,而b_pre是b遞增之後 的值。這就是++的字首形式和字尾形式的區別
x*y++表示的是(x)*(y++),而不是(x+y)++。不過後者無 效,因為遞增和遞減運算子只能影響一個變數(或者,更普遍地說,只能影 響一個可修改的左值),而組合x*y本身不是可修改的左值。
不要混淆這兩個運算子的優先順序和它們的求值順序。假設有如下語句:
y = 2;
n = 3;
nextnum = (y + n++)*6;
nextnum的值是多少?把y和n的值帶入上面的第3條語句得:
nextnum = (2 + 3)*6 = 5*6 = 30
n的值只有在被使用之後才會遞增為4。根據優先順序的規定,++只作用 於n,不作用與y + n。除此之外,根據優先順序可以判斷何時使用n的值對錶達 式求值,而遞增運算子的性質決定了何時遞增n的值。
如果n++是表示式的一部分,可將其視為“先使用n,再遞增”;而++n則表示“先遞增n,再使用”。
表示式和語句
表示式
表示式(expression)由運算子和運算物件組成(運算物件是運算子操作的物件)。最簡單的表示式是一個單獨的運算物件,以此為基礎可以建立複雜的表示式
運算物件可以是常量、變數或二者的組合。一些表示式由子 表示式(subexpression)組成(子表示式即較小的表示式)
語句
語句(statement)是C程式的基本構建塊。一條語句相當於一條完整的 計算機指令。在C中,大部分語句都以分號結尾
語句可分為簡單語句和複合語句。簡單語句以一個分號結尾。
如下所示:
賦值表示式語句: toes = 12;
函式表示式語句: printf("%d\n", toes);
空語句: ; /* 什麼也不做 */
複合語句(或塊)由花括號括起來的一條或多條語句組成。
如下面的 while語句所示:
while (years < 100)
{
wisdom = wisdom * 1.05;
printf("%d %d\n", years, wisdom);
years = years + 1;
}
型別轉換
基本的型別轉換規則
1.當型別轉換出現在表示式時,無論是unsigned還是signed的char和short 都會被自動轉換成int,如有必要會被轉換成unsigned int(如果short與int的大 小相同,unsigned short就比int大。這種情況下,unsigned short會被轉換成 unsigned int)。
在K&R那時的C中,float會被自動轉換成double(目前的C不是這樣)。由於都是從較小型別轉換為較大型別,所以這些轉換被稱為升級 (promotion)。
2.涉及兩種型別的運算,兩個值會被分別轉換成兩種型別的更高級別。
3.型別的級別從高至低依次是long double、double、float、unsignedlong long、long long、unsigned long、long、unsigned int、int。例外的情況是,當 long 和 int 的大小相同時,unsigned int比long的級別高。
之所以short和char型別沒有列出,是因為它們已經被升級到int或unsigned int。
4.在賦值表示式語句中,計算的最終結果會被轉換成被賦值變數的類 型。這個過程可能導致型別升級或降級(demotion)。所謂降級,是指把一 種類型轉換成更低級別的型別。
5.當作為函式引數傳遞時,char和short被轉換成int,float被轉換成 double。函式原型會覆蓋自動升級。
型別升級通常都不會有什麼問題,但是型別降級會導致真正的麻煩。原因很簡單:較低型別可能放不下整個數字。例如,一個8位的char型別變數儲存整數101沒問題,但是存不下22334。
待賦值的值與目標型別不匹配時規則
1.目標型別是無符號整型,且待賦的值是整數時,額外的位將被忽略。 例如,如果目標型別是 8 位unsigned char,待賦的值是原始值求模256。
2.如果目標型別是一個有符號整型,且待賦的值是整數,結果因實現而異。
3.如果目標型別是一個整型,且待賦的值是浮點數,該行為是未定義的