第二章C語言編程基礎知識
本章介紹的是用C語言編程必須掌握的一些基礎知識,包括最基本的數據類型、對數據進行運算的運算符和數據的輸入輸出。
C語言中數據類型雖然比較豐富,但也很容易掌握;C語言的運算符主要是大家比較熟悉的算術運算符和邏輯運算符;至於輸入輸出,輸出內容就是對特定問題進行解答而得到的結果,輸入內容就是對特定問題進行求解時所必須接受的數據。
2.1 C語言的數據類型
數據類型是程序設計中的一個重要概念,它規定了該類型中數據的值域。例如,數值類型的數據,其值域是計算機所能表示的數的範圍內的所有數據;邏輯類型的數據取值範圍是“真”(TRUE)或“假”(FALSE);字符類型的數據值域是某一字符集中的所有元素;指針類型的數據值域是計算機存儲單元的絕對地址或相對地址的集合。
數據類型定義了一個運算集。例如,對數值型數據可施加算術運算;對邏輯型數據可施加邏輯運算;對字符型數據可施加連接和求子串運算;對指針型數據準許進行加、減運算,而不準許進行乘除運算等。當然,不同數據類型也可以進行混合運算,其結果為數據類型中字節最多的數據類型。
數據類型同時也定義了數據在內存中的存儲方式。例如,一個字符型數據在計算機內存中占一個字節。長度為n的字符串在計算機內占用連續的n+1個字節。一個整型數在計算機內存中占2個字節,一個單精度浮點數在計算機內存中占4個字節,一個雙精度浮點數在計算機內存中占8個字節等。
在高級語言中,每一個數據都屬於一定的數據類型,不存在不屬於某種數據類型的數據。
C語言提供如圖2-1所示的數據類型。數據包含常量和變量,它們都屬於上述某種數據類型,本章主要介紹基本數據類型,其他數據類型將在以後的章節中逐步介紹。
圖2-1 C語言的數據類型
2.2 常 量
常量是程序中其值不發生變化的量。在C語言中,常量有數、字符和字符串三種。除此之外,C語言程序中還經常使用另外一種表示形式的常量,叫符號常量,它由宏定義語句define來定義。
2.2.1 數
C語言中的數有整型數和實型數兩種,其中實型數還分為單精度實型數和雙精度實型數,其有效數值範圍也不一樣。
1.整型數
整型數有十進製數、八進製數和十六進製數三種,其表示形式如表2-1所示。
表2-1 整型數的三種形式
進 製 表達方式 樣 例
八進製數 由數字0開頭 012,022
十六進製數 由0x或0X開頭 0x12,0x22,0xff
十進製數 必須是數字1,2…9之中的一個數開頭 12,22
值得注意的是,不同進製的數,表麵上看數字是一樣的,如012、0x12和12,它所代表的真正數值是不同的。例如:
012是八進製數,其代表的十進製數為10;
0x12是十六進製數,其代表的十進製數為18;
12是十進製數。
整型數又可分為正整型數和負整型數,分別在其前麵加上:“+”或“-”表示,正整型數的符號“+”可省略。例如:
444,+666,-111,0222,-0222,0x3bf,-0x4de
整型數有短整型數、一般整型數和長整型數。對多數計算機係統而言,短整型數一般占用兩個字節,一般整型數占用兩個字節(即16位二進製位),其取值範圍是-215~215-1即-32768~32767。超過該範圍的整型數用長整型數表示。長整型數占用4個字節,其取值範圍是-231~231-1,即-2147483648~2147483647。長整型數的表示方法是在數的末尾加一個字符I或L,例如:
218I 333L 0x7dfL
2.實數
實數又稱之為浮點數,隻用在十進製數中。它有單精度和雙精度之分。其表示形式分為一般形式和指數形式兩類,指數形式也稱為科學計數法。一般形式的實數由整型數部分、小數點和小數部分所組成。例如:
3.14159 .0666-678.777 999.0 888. 0.88
指數形式的實數由尾數、e或E和指數三部分組成,例如:
0.55e5 333E-3 8.88e+18
其中0.55、3.33和8.88為尾數,e或E後麵的5、-3和+18均是指數。值得注意的是,用指數形式表示的浮點數必須有尾數。
2.2.2 字符常量
字符常量是用一對單引號括起來的單一字符,它在計算機的存儲中占據一個字節。單引號是定界符,它並不是字符常量的一部分。下麵顯示的都是字符常量:
'a','A','?','2','*'
字符常量的值就是該字符在其所屬字符集(如ASCII)中的編碼,例如'A'的ASCII值是65,'a' ASCII值是97,'2'的ASCII值是50等。由於字符常量中的單引號(')已作為定界符使用,於是單引號的字符常量表示形式為“\'”;而反斜杠(\)的字符常量表示形式為“\ \”。所以“'”和' \ '都是錯誤的表示形式。由於字符常量在計算機中是以其編碼形式存放,因此可以說一個字符常量實際上是一個字節的整型數,它可以參與各種運算,例如:
x='b';
y='b'+15;
z='?'+'A';
它們分別相當於下列運算(在運算過程中將字符型數據轉化成其相應的ASCII值參與運算):
x=98;
y=98+15;
z=63+65;
在C語言程序中,字符常量通常用於字符之間的比較。
一般來說,字符可以直接寫出,如“A”和“B”等。但製表符、回車-換行字符、退格字符等一些控製字符卻不能直接寫出,為此,C語言提供了一類轉義字符或稱換碼序列,用於表示那些無法在鍵盤上直接表示的字符。這些特殊字符有常用的控製字符(它們沒有相應的印刷符號)和用於功能定義的字符(如單引號、雙引號、反斜線等),用轉義字符表示法就可在字符常量和字符串常量中書寫它們了。常用的轉義字符如表2-2所示。
表2-2 轉義字符
轉換字符 含 義
回車-換行
\t 水平製表(TAB)
\v 垂直製表
\b 退格字符(向左刪除一字符)
\r 回車
\f 換頁
\a 響鈴警報
\' 單引號(')
\“ 雙引號(”)
\0 空字符(NULL)
\ddd 表示一個字節的代碼(或一個字符代碼),其中ddd為三位八進製數,如\101表示字符A,\010表示\b
\xddd 也是表示一個字節的代碼(或一個字符代碼),其中ddd為三位十六進製數,如\x041表示字符A,\x008表示\b
2.2.3 字符串常量
字符串常量是用一對雙引號括起來的一串字符,字符的個數稱為字符串的長度,字符串常量簡稱字符串,下列所示都是字符串常量:
“Tsinghua”、“C Program”、“University”
長度為n的字符串,在計算機的存儲中占用n+1個字節,分別存放各字符的編碼,最後一個字節是NULL字符(或叫空字符,該字符在ASCII字符集中的編碼為0。為了書寫方便,在C語言程序中用'\ 0 '來表示該字符)。也就是說,任何一個字符串在機內都是以' \ 0'結尾。
雙引號和反斜線字符在字符串中的表示形式類似單引號和反斜線在字符常量中的表示形式,應該以“\””或“\ \”的形式出現,而不應該是“””或“\”。例如字符串:
“\”Tsinghua University\””
其中\”表示雙引號字符
由上所述可知,字符常量與字符串常量在表示形式和存儲形態上是不同的,例如'A'和“A”是兩個不同的常量。字符常量'A'可以賦給字符型變量,而字符“A”隻能賦給字符型數組。字符串“”表示空串,它在存儲中占一個字節,其值為NULL字符的代碼。
2.2.4 符號常量
在C語言程序中,可對常量進行命名,即用符號代替常量。該符號叫符號常量。符號常量一般用大寫字母表示,以便與其他標識相區別。符號常量要先定義後使用,定義的一般格式是:
#define 符號常量 常量
例如:
# define NULL 0
#define EOF -1
# define PI 3.1415926
這裏的# define是預編譯命令,一個#define命令隻能定義一個符號常量,且用一行書寫,不用分號結尾。符號常量一旦定義,就可在程序中代替常量使用。
【例2-1】求圓柱體體積的程序。
# define PI 3.1415926 / * 定義符號常量,即宏定義 * /
main()
{
float r,h,v;
scanf(" % f % f ",& r,& h);
v=PI * r * r * h; / * 求體積時用到了常量 */
printf("Volume= % f ",v);
}
使用符號常數有如下兩點好處:
?增強可讀性:符號常量在程序中代替具有一定含義的常量,如用EOF代替-1(表示結尾),用PI代替3.1415926等,可增強程序的可讀性,而且值在程序中不變。
?增強程序的可維護性:如果一個大的程序中有多處使用同一個常量,這時可把該常數定義為符號常數。這樣,當需要對某一個數在多處進行修改時,隻需在其定義的地方做修改即可,不必做多處改變,這樣可以避免偶然的錯誤。當調試、擴充或移植一個程序時,如果需經常改變某些常量的話,則把它定義為符號常量將大有好處。
2.3 數據類型及變量
數據類型及變量是C語言編程中首先麵對的問題,下麵將分別介紹。
2.3.1 基本數據類型
在2.1節中我們已簡要地列出了C語言的數據類型。本節將進一步說明基本數據類型。C語言的基本數據類型有如下不同的類型:
?從長度上分,有8位、16位、32位和64位。
?從數據的符號來分,有無符號數和有符號數。
?按照數據的數學性質,分為整型、實型和字符型。
數值型數據的類型及表示形式等如表2-3所示。C語言中各種基本數據類型的長度和範圍隨CPU的類型和編譯器的實現不同而異,但對於大多數微機而言,其長度和範圍如表2-3所示。
表2-3 C語言的基本數據類型
類型標識符 名 字 長度(二進製位) 範 圍
Char 字符型 8 ASCII字符代碼
Unsigned char 無符號字符型 8 0至255
Signed char 有符號字符型 8 -27至27-1
Int 整型 16 -215至215-1
Unsigned int 無符號整型 16 0至216-1
Singned int 有符號整型 16 同int
Short int 短整型 16(有的8) 同int
unsigned short int 無符號短整型 16(有的8) 同unsigned int
signed short int 有符號短整型 16(有的8) 同short int
long int 長整型 32 -231~231-1
singned long int 有符號長整型 32 同long int
unsigned long int 無符號長整型 32 0至232-1
float 浮點 32 10-38~1038
double 雙精度型 64 10-308~10308
void 空值型 0 無值
void類型有兩種用法:其一是指定函數返回值的類型,其二是用來設置類屬指針。
2.3.2 變量及變量的定義
變量是在程序執行過程中,其值可能發生變化的一種量。每一個變量都對應計算機內存中相應長度的存儲單元,以存放變量所取的值。下麵是一個變量x在程序中的應用實例。
main()
{
float x;
x=2.001;
x=2*x;
x=x+2.2;
printf("f",x);
}
每一個變量都用一個名字來標識,稱之為變量名。變量名實質上是計算機內存單元的命名,變量的地址就是該內存單元的開始地址。變量名命名規則與用戶定義標識符一樣。
同常量一樣,任何一個變量都屬於某一數據類型。若它為整型變量,則在其作用域內隻能取整型數值;若定義它為實型變量,則在其作用域內隻能取實數值。
變量定義就是按照特定的方式為其使用的變量指定標識名、類型和長度等。在C語言程序中,所使用的每一個變量在引用前都必須先定義,否則編譯程序將在程序編譯時發出錯誤提示信息。
簡單變量定義的方法是在類型標識後跟一個變量或變量表,變量之間用逗號分開,然後以分號結尾。下麵是一些變量定義的例子:
int x,y,z; / *定義整型數變量x,y和z,變量表之間用逗號隔開* /
float sum,average; / *定義實型變量sum和average * /
double voleme; / *定義雙精度型變量volume* /
unsigned long distance; / *定義無符號長整型數distance* /
char cl,c2; / *定義字符型變量c1和c2* /
2.3.3 變量的初始化
上述的變量定義隻是指定了變量名字和數據類型,並沒有給它們賦初值,給變量賦初值的過程稱為初始化。值得注意的是,沒有賦初值的變量並不意味著該變量中沒有數值,而隻表明該變量中尚未定義的值。因此,在使用該變量時,它所標識的內存單元尚保留先前使用該單元時留下的內容,於是,引用這樣的變量就可能產生莫名其妙的結果,C語言準許在定義變量時對其初始化。例如:
int a=2001; / *定義變量a為整型,初始值為888 */
double p=15.5,d=0.1; / *定義p和d為雙精度實型,初始值分別為15.5和0.1*/
float x ,y,z=4.53; / *隻給一個變量設置初始值*/
short int i=j=k=555; / *給i,j,k設置同一個初始值555*/
char c='a'; / *定義字符型變量c,其值為字符a*/
2.4 數據類型轉換
在C語言的表達式中,準許對不同類型的數值型數據進行某一操作或混合運算。當不同類型的數據進行操作時,應當首先將其轉換成相同的數據類型,然後進行操作。數據類型轉換有兩種形式,即隱式類型轉換和顯示類型轉換。
2.4.1 隱式類型轉換
所謂隱式類型轉換就是在編譯時由編譯程序按照一定規則自動完成,而不需人為幹預。因此,在表達式中如果有不同類型的數據參與同一運算時,編譯器就在編譯時自動按照規定的規則將其轉換為相同的數據類型。
C語言規定的轉換規則是由低級向高級轉換。例如,如果一個操作符帶有兩個類型不同的操作數時,那麼在操作之前行先將較低的類型轉換為較高的類型,然後進行運算,運算結果是較高的類型。更確切地說,對於每一個算術運算符,則遵循圖2-2所示的規則。
圖2-2 數據類型轉換規則之一
注意:在表達式中,所有的float類型都轉換為double型以提高運算精度。
在賦值語句中,如果賦值號左右兩端的類型不同,則將賦值號右邊的值轉換為賦值號左邊的類型,其結果類型還是左邊類型。
因為函數參數是表達式,因此,當參數傳遞給函數時,也發生類型轉換。具體地說,char和short均轉換為int;float轉換為double。這就是為什麼我們把函數參數說明為int和double,盡管調用函數時用char和float .
也可以將圖2-2所示的規則用圖2-3表示。圖2-3中的水平箭頭表示必定轉換,縱向箭頭表示兩個操作對象類型不同時的轉換方向。
圖2-3 數據類型轉換規則之二
下麵舉行說明類型轉換的規則。例如執行:
x=100+'a'+1.5 * u+f/'b'-s * 3.1415926
其中,u為unsigned型,f為float型,s為short型,x為float型。式中右麵表達式按如下步驟處理:
(1)首先將'a'、'b'和s換成int,將1.5和f轉換為double型。
(2)計算100+'a',因'a'已轉換為int型,於是此運算結果為197。
(3)計算1.5*u,由於1.5已轉換為double,u是unsigned型,於是首先u轉換為double,然後進行運算,運算結果為double。
(4)計算197+1.5 * u,先將197轉換為double(如197.00…00),其結果為double。
(5)計算f/ 'b',f已轉換為double,'b'已轉換為int,於是先將'b'再轉換為double,其結果為double。
(6)計算(197+1.5 * u)+f / 'b',者均為double,於是結果也為double。
(7)計算s * 3.1415926,先將s由int轉換為double,然後進行運算,其結果為double。
(8)最後與前麵得的結果相減,結果為double。
(9)最後將表達式的結果轉換為float並賦給x。
2.4.2 顯式類型轉換
顯示類型轉換又叫強製類型轉換,它不是按照前麵所述的轉換規則進行轉換,而是直接將某數據轉換成指定的類型。這可在很多情況下簡化轉換。例如,
int i;
…
i=i+9.801
按照隱式處理方式,在處理i=i+9.801時,首先i轉換為double型,然後進行相加,結果為double型,再將double型轉換為整型賦給i。
int i;
…
i=i+(int)9.801
這時直接將9.801轉換成整型,然後與i相加,再把結果賦給i。這樣可把二次轉換簡化為一次轉換。
顯示類型轉換的方法是在被轉換對象(或表達式)前加類型標識符,其格式是:
(類型標識符)表達式
例如,有如下程序段:
main()
{
int a,b;
float c;
b=a+int(c);
printf("b=d% \ n",b);
}
在上述程序的運行過程中,在執行語句b=a+int(c)時,將c的值臨時強製性轉化為int型,但變量c在係統中仍為實型變量,這一點很重要,不少初學者在這個問題上忽略了這個問題。
2.5 運算符和表達式
2.5.1 運算符和表達式概述
1.表達式
一個表達式包含一個或多個操作,操作的對象稱作運算元(或叫作操作數),而操作本身通過運算符體現的。例如a、a-b、c=9.801等都是一個表達式。
一個表達式完成一個或多個操作,最終得到一個結果,而結果的數據類型由參加運算的操作決定。最簡單的表達式是隻含一個常量或變量的表達式,即隻含一個操作數而不含運算符。
C語言中表達式的種類十分豐富,主要有如下一些:
?算術表達式:進行一般的計算。
?賦值表達式:進行賦值操作。
?關係表達式:進行比較判斷。
?邏輯表達式:進行邏輯比較判斷。
?條件表達式:進行條件滿足與否的判斷。
?逗號表達式:實際上是一種複雜運算,可以包含多個算術表達式。
2.C語言的操作符
C語言的特點之一是具有豐富和使用靈活的運算符,概括起來它有如下的幾類運算符:
?算術運算符。
?賦值運算符(包括符合賦值運算符)。
?關係運算符。
?邏輯運算符。
?條件運算符。
?逗號運算符。
?位運算符。
?指針運算符。
?求字節運算符(可以歸並到函數的應用中去,它是通過函數sizeof()來進行運算的)。
?強製類型轉換運算符。
這些運算符如表2-4所示。
表2-4 C語言中的運算符
名 稱 操作符
自增,自減 + +,- -
邏輯與、或、非 & &,︱︱,!
續 表2-4
名 稱 操作符
指針操作及引用 *,&
加、減、乘、除、求模運算 +,-,*,/,%
關係操作符 <,<=,>,>=,= =,! =
按位與、或、異或、求反 &,丨,^,~
逗號表達式 ,
類型轉換 ()
移位運算 < <,> >
條件運算 ? :
求占用的字節數 sizeof
賦值 =,+ =,- =,*=,/ =,% =
2.5.2 算術運算符及算術表達式
1.基本算術運算符
+、-、*、/和求模(%)運算為基本算術運算符,當進行兩個整型數相除時,其結果仍為整型數.小數部分被略去。例如:
27/5=5
27/7=3
取模運算符“%”用於計算兩個數相除後得到的餘數,隻適用於兩個整型數取模,不能用於其他數據的運算。在“x%y”表達式中,x是被除數,y是除數。
2.自增自減運算符
自增“++”和自減“――”運算符為變量的增1和減1提供了緊湊而方便的表達形式。但這兩種運算符都有前置和後置之分,其一般用法如下:
i++或++ i
i――或――i(相當於i=i-1)
其中i是一個整型變量。對一個變量實行前置或後置運算其結果是相同的,即都使它增1或減1。前置運算是在該變量參與其他運算之前先增1或減1,而後置運算是在它參與其他運算之後才增1或減1。這一點是要區別的。例如:
k=3;
j=5;
i=3;
m=(++k)* j;
n=(i++k)* j;
在計算m=(++k)* j時,k首先增1變為4,然後與j相乘,最後將20賦給m。在計算n=(i++k)* j時,由於對i實施的是後置運算;因此i是用3的值參與乘運算的。於是(i++)*j=3*5=15。在參與乘操作之後,i才增1變為4;最後賦給n的值是15。下邊是自減的例子:
i=4;
j=5;
k=4;
m=(i--)* j;
n=(--k)* j;
在計算m=(i--)* j時,由於對i實施的後置減1,所以(i--)* j=4*5=20。i在參與乘之後才減1,此時m=20,i的值變為3;在計算n時,k首先減1,變為3,然後與j進行乘,結果為15,於是n=15。
使用自增自減運算時,應注意如下幾點:
?自增自減運算的操作數一定是變量,而不能是其他表達式。例如,
i++ / *是合法的* /
(i+j)++ / *非法的,自增或自減隻對變量進行* /
10++ / *是非法的* /
?一個變量的前、後置運算隻表明該變量參與其他運算與其自身變化之間的先後關係,並不影響它在表達式中參與其他運算的順序。
?“++”和“--”運算符是自右向左結合的,單目運算符“-”也是自右向左結合的。因此,對-i++就理解為-(i++),而不應理解為(-i)++。(-i)++是非法的,因為它是表達式而不是變量。
?自增和自減運算常用於數組下標改變循環次數控製。例如,
x=10;
i=5;
…
a[i++]=x; / *把10賦給a[5],然後I變為6*/
對於下麵的運算:
int m=5,n;
n=(m++)+(m++);
其計算過程是先進行m的三次相加,5+5+5=15,即n=15,然後再進行m的三次自加,最後m的值為6。對於下麵的運算: