C語言簡明教程
(二)
數據類型
從本章開始我們將要引入大量的 C 語言程序案例,把算法和語法結合起來,通過引導大家由淺入深地編寫 C 程序,讓大家掌握 C 語言。也希望大家在學習的過程中勤動手,把每一個案例都自己來寫一遍。
我們接下來將講述 C 語言的順序程序設計,由於 C 語言的順序程序設計並不容易獨立成章,也需要大家具有數據類型和表達式等相關的基礎知識,所以我們將順序程序設計這一大的章節分為以下幾個小節同時融入順序程序設計的相關例題。
知識點接下來我們就開始進行這一章的知識點講解,配合代碼編寫深入掌握 C 語言的輸入輸出、常量變量以及包含整數型、字符、字符串、浮點數等在內的數據類型。
首先我們先舉一個例子 3-1,下面我們建立一個程序 3-1.c,輸入以下代碼:
輸入以下代碼:
#include<stdio.h>
int main(){
int a,b,c;
printf("Please enter a value:");
scanf("%d",&a);
printf("\n");
printf("Please enter b value:");
scanf("%d",&b);
c = a + b;
printf("%d\n",c);
return 0;
}
編譯並運行:
如果該程序成功運行,首先終端會顯示 Please enter a value: 提示大家輸入 a 的值,大家寫一個整數(注意是整數),然後終端會顯示 Please enter b value: 提示大家輸入 b 的值,之後將會運算 c = a + b 的結果。如下圖輸入 4 和 5 將計算出 c 的值為 9:
接下來,我們依託這個程序講解幾個知識點,之後再做分析。
格式輸出函數 printf()一般形式:printf(格式控制,輸出表列)。例如:printf("%d,%d",a,b); 括號內包含兩個部分:
(1)格式控制是用雙引號括起來的一個字符串,稱「轉換控制字符串」,簡稱「格式字符串」,它包括兩個信息:
(2)輸出表列是程序需要輸出的數據。看下面例子:
printf("I love %d and %d",x,s);
第一個 %d 對應的是 x 的值,第二個 %d 對應的是 s 的值。I love 和 and(注意這裡包括空格)都是普通字符會原樣輸出。
假如 x 的值是 3,s 的值是 4,這條語句將會輸出 I love 3 and 4。
格式輸入函數 scanf()一般形式:scanf(格式控制,地址表列)。格式控制的含義同 printf() 函數。「地址表列」是由若干地址組成的表列,可以是變量的地址。
看下面的例子:
scanf("a=%d,b=%d",&a,&b);
在格式字符串中除了有格式聲明的 %d 以外,其它普通字符在賦值時需要原樣輸入(如「 a= 」,「 b= 」和「,」),假如給 a 和 b 分別賦值 5 和 6,將輸入 a=5,b=6。建議大家不要在格式控制中加過多的普通字符,否則會發生不可預料的 BUG。
注意:scanf() 函數中的表列是地址表列。 scanf("a=%d,b=%d",&a,&b); 中 a 和 b 前面的 & 不能省掉,這一點要和 printf 作區分。
printf() 函數和 scanf() 函數我們會在後續深入講解。
注釋位於 /* */ 中的和 // 後面的內容為注釋,用來對代碼進行說明,注釋在編譯時會被自動忽略。
3-1.c 是一個簡單的計算程序,通過定義變量讓用戶可以自由設定 a 和 b 的值,之後通過 c = a + b; 這條語句實現把 a 和 b 的和計算出來並賦值給 c。究竟什麼是變量,什麼是常量呢?接下來我們來一一講述。
常量
顧名思義,值不能被改變的量稱為常量。如 5、7、19 或者 0.54、4.33 這些值,常見的常量分為以下類型:
整型常量如 0、100、-30 等整數都是整型常量。
實型常量就是我們通常所說的小數,如 12.34, -5.45, 143.342 等,小數還可以用指數形式表現,如 32.23e3(表示 32.23*10^3),-323.34e-6(表示 -323.34*10^-6),由於計算機無法表示上角和下角,所以規定以字母 e 或者 E 代表以 10 為底的指數。
注意:e 或者 E 之前必須有數字,且 e 或者 E 後面必須為整數,不能是 e3 或者 12e4.1 這種形式。
字符常量,字符常量有兩種普通字符:用單引號括起來的一個字符,如 'a'、'E'、'%'、'3'。不能寫成 'ab'、'12'。字符常量只能是一個字符,不包括單引號。
轉義字符:除了以上形式的字符常量外,C 語言還允許用一種特殊形式的字符常量,就是以字符 \ 開頭的字符序列,比如我們本節課的 3-1.c 中,\n 代表的就是換行符,顯示跳轉到下一行。這是一種在屏幕上無法顯示的「控制字符」。
轉義字符含義轉義字符含義\n換行\t水平制表(右移 8 格)\v垂直制表\b退格\r回車(不換行)\f換頁\a響鈴\\反斜線\'單引號\''雙引號\add3 位 8 進位數代表的字符\xhh2 位 16 進位數代表的字符變量
什麼是變量在例子 3-1 中的 a,b,c 都是變量。變量代表一個有名字的、具有特殊屬性的存儲單元。它可以用來保存數據。變量的值是可以改變的。變量在程序中定義的一般形式就是:<類型名稱> <變量名稱>。例如:
int a; int b; int a,b; int price; int amount; ...
變量需要一個名字,變量的名字便是一種「標識符」,用來區別它和其它不同的變量。用來對變量、函數、數組等命名的字符序列統稱為標識符,上面提到的 price 、amount 是標識符,函數名 printf 也是一種標識符。C 語言規定標識符只能由字母、數字和下劃線構成,且第一個字符必須為字母或下劃線。¥ qa,1ew,#22 這些都是非法的標識符。
變量的賦值與初始化變量必須先定義後使用。這一行定義了一個變量,變量的名字是 price,類型是 int,初始的值是 0。
注意:和數學不同,a=b 在數學中表示關係,即 a 和 b 的值一樣;而在程序設計中,a=b 表示要求計算機做一個動作:將 b 的值賦值給 a。關係是靜態的,而動作是動態的。在數學中,a=b 和 b=a 是等價的,而在程序設計中,兩者意思相反。
數據類型什麼是數據類型我們之前的案例中講到了整數類型 int 定義整型變量,在程序中我們還會用到浮點類型(float)來表示具有小數點的實數,講解數據類型之前我們先來看一個用到浮點型數據的例子 3-2:
一臺拖拉機耕地一畝耗油 0.85kg,它的油箱的容積是 100 升(0.1m3),柴油的密度是 850kg/m3,該拖拉機裝滿油後最多耕地的畝數是多少?
我們在函數中首先要定義幾個變量,定義油箱的容積的變量為浮點型類型 tank_volume,油的密度為浮點類型 oil_density,油箱裝在油的總質量為浮點型 oil_kg,該拖拉機裝滿油最多耕地的畝數是浮點型 area。下面我們建立一個程序 3-2.c 並輸入以下代碼:
#include<stdio.h>
int main(){
float tank_volume; // 定義油箱的容積為浮點類型變量 tank_volume
float oil_density; // 定義油的密度為浮點類型變量 oil_density
float oil_kg;
float area;
tank_volume = 0.1; // 給變量 tank_volume 賦值
oil_density = 850; // 給變量 oil_density 賦值
oil_kg = tank_volume*oil_density; // 求 tank_volume 和 oil_density 的積並賦值給 oil_kg
area = oil_kg/0.85; // 求 oil_kg/0.85 的商並賦值給 area
printf("Most farming is %f mu",area);
return 0;
}
代碼說明:下面我們來解釋這個程序。下面的語句定義了四個變量:
float tank_volume;
float oil_density;
float oil_kg;
float area;
下面這兩條語句是給 tank_volume 和 oil_density 這兩個變量賦值:
tank_volume = 0.1; // 給變量 tank_volume 賦值
oil_density = 850; // 給變量 oil_density 賦值
其實 tank_volume=0.1; 與 oil_density=850; 這兩條語句可以和前面的 float tank_volume; 與 float oil_density; 這兩條語句放在一起,定義變量的同時初始化賦值,即:
float tank_volume = 0.1;
float oil_density = 850;
接下來我們使用乘法運算符 * 將油箱的容積和油的密度相乘,計算出油箱可以存放油的總質量:
oil_kg = tank_volume*oil_density;
// 求 tank_volume 和 oil_density 的積並賦值給 oil_kg
之後我們利用除法運算符 / 求出最多可耕地的畝數,並賦值給 area:
為什麼在用計算機運算時,需要指定數據的類型呢?
在數學中,數值是不區分類型的,數值的運算是絕對準確的,例如:1/3 的值是 0.33333...(循環小數)。數學是研究抽象的學科,數和數的運算都是抽象的。而在計算機中,數據是存儲在計算機中的一個個單元裡面,它是具體存在的。而且,存儲單元是由有限的字節構成的,每一個存儲單元存儲的數據是有限的,不可能存放無限大的數,也不能存放無限循環小數,例如計算和輸出 1/3:
得到的結果是 0.333333,只能得到六位小數,而不是無窮位的小數。
大家可以大致瀏覽該圖,不需要現在背下來這些數據類型。接下來我們將要講述基本類型裡面的整型和浮點型。
整數類型
為了方便大家理解數據在計算機中的存儲方式,我們首先給大家講述一下計算機內存。計算機在執行程序的時候,組成程序的指令和程序所操作的數據都必須存儲在某個地方,這個地方就是計算機的內存,也稱為 RAM。
可以將計算機的 RAM 想像成一排井然有序的盒子。每個盒子都有兩個狀態:滿為 1,空為 0 ,因此每個盒子代表一個二進位數:0 或 1 。計算機有時用真和假表示它們:1 為真,0 為假。每個盒子稱為一個位(bit)。每 8 個位組成一個字節,在計算機中,一個英文字母(不分大小寫)佔一個字節的空間,一個中文漢字佔兩個字節的空間。
計算機中常用的單位是千字節(KB)、兆字節(MB)、千兆字節(GB)。這些單位的意義如下:1KB 是 1024 字節。其中 1024=2^10(2 的 10 次方),1MB=1024KB,1GB=1024MB 。如果大家對二進位、字節這些概念不是很熟悉,可以點擊查看字節、二進位學習了解相關概念。
基本類型(int 類型)編譯系統分配給 int 類型數據 2 個字節或者 4 個字節(由具體的編譯系統自行決定)。我們使用的 gcc 編譯器為每個整數類型分配四個字節(32 個二進位)。在存儲單元中的存儲方式是:用整數的補碼形式存放。所以當 4 個字節的整數類型取值範圍是 -2^31 到(2^31-1)。無符號的基本整型表示為 unsigned int,和 int 類型佔有的字節數相同,取值範圍是 0 到(2^32-1)。
短類型(short 類型)短整型的類型名為 short,gcc 編譯系統分配給 short 類型 2 個字節,存儲方式和 int 類型一樣,也是補碼的形式存儲,取值範圍是 -2^15 到(2^15-1),無符號短整型 unsigned short 的取值範圍是 0 到(2^16-1)。
長整型(long 類型)gcc 編譯系統分配給 long 類型 8 個字節,存儲方式和 int 類型一樣,也是補碼的形式存儲,取值範圍是 -2^63 到(2^63-1),無符號長整型 unsigned long 的取值範圍是 0 到(2^64-1)。
不同類型佔用的空間在這裡大家可以通過 sizeof() 運算符查看各類型的常量佔據多少字節。
創建 3-3.c 文件並輸入以下代碼:
#include<stdio.h>
int main(){
printf("%d\n",sizeof(int));
printf("%d\n",sizeof(short));
printf("%d\n",sizeof(long));
return 0;
}
保存後編譯運行顯示以下結果:
4,2,8 代表 int,short,long 佔用的字節數。
由於 sizeof() 的值的類型不是 int 型,出現了 warning。對於編譯中出現的 warning 警告,我們需要加以注意,但依然可以生成可執行文件,在本次實驗中,我們暫不處理,繼續運行。
警告解決方案:
(int)sizeof(type); //可以將 sizeof() 值類型強制轉換成int型
printf("%d\n",(int)sizeof(int)); //這樣不會報警告,希望大家能舉一反三
浮點型數據是用來表示具有小數點的實數的。想知道為什麼在 C 中把實數稱為浮點數嗎?
在 C 語言中,實數是以指數的形式存放在存儲單元的。一個實數表示為指數可以不止一種形式,如 4.3242 可以表示為 4.3242*10^0,0.43242*10^1,0.043242*10^2, 432.42*10^-2 等,他們代表同一個值。可以看到小數點的位置是可以在 43242 幾個數字之間浮動的,只要在小數點位置浮動的同時改變指數的值,就可以保證它的值不會改變。由於小數點的位置可以浮動,所以實數的指數形式稱為浮點數。
規範化的指數形式:在指數形式的多種表示方式中把小數部分中小數點前的數字為 0,小數點後第 1 位數字不為 0 的表示形式稱為規範化的指數形式,如 0.43242*10^1 就是 4.3242 的規範化的指數形式。一個實數只有一個規範化的指數形式。
浮點數類型包括 float(單精度浮點型)、double(雙精度浮點型)、long double(長雙精度浮點型)。
float 型(單精度浮點型)gcc 編譯系統為每一個 float 型變量分配 4 個字節,數值以規範化的二進位數指數形式存放在存儲單元中。在存儲時,系統將實型數據分成小數部分和指數部分兩個部分、分別存儲。如 3.14159 在內存中的存放形式如下圖:
圖中是用十進位來示意的,實際在計算機中是用二進位數來表示小數部分以及用 2 的冪次來表示指數部分的。在 4 個字節(32 位)究竟用多少位表示小數部分,多少位表示指數部分,是由 c 語言編譯系統自定的。
如果想要知道 float 的取值範圍,我們可以編寫 4-1.c 程序:
#include<stdio.h>
#include<float.h>
int main(){
printf("The size of the smallest positive non-zero value of type float is %.3e\n",FLT_MIN);
printf("The size of the largest value of type float is %.3e\n",FLT_MAX);
return 0;
}
如果程序你成功運行的話,你將會看到 float 數據類型的取值範圍。現在我們試著編寫一個計算題吧!
我們要做的是利用輸入的直徑計算一個圓桌的周長及面積。計算圓的周長或者面積時,數學公式要使用 pi(周長 = 2*pi*r,面積 = pi*r^2,其中 r 是半徑)。如果不記得這些公式也不用擔心。這不是數學課本,所以只要理解程序是如何運行的即可。
編寫 4-2.c 程序:
#include<stdio.h>
int main(){
float radius,diameter;
float circumference,area;
float pi = 3.1415926;
printf("Input the diameter of the table:");
scanf("%f",&diameter);
radius = diameter / 2.0;
circumference = 2.0 * pi * radius;
area = pi * radius * radius;
printf("\nThe circumference is %f",circumference);
printf("\nThe area is %f",area);
return 0;
}
為了能擴大數字的範圍,用 8 個字節(64 位)存儲一個 double 型數據,可以得到 15 位有效數字,double 型的數值範圍大家可以按照 4-1 案例查看,其中 double 極限值符號的下限為:DBL_MIN,上限為 DBL_MAX。double 型的存儲方式和 float 的存儲方式相同。
浮點型數據所佔的內存空間以及取值範圍:
在平時我們使用的浮點型數據的時候要注意這幾個點哦!超過有效位的數字被捨去,可能產生捨入誤差
編寫 4-3.c 程序,輸入以下代碼:
#include <stdio.h>
int main(){
float a , b ;
a = 123456.789e5 ;
/* 相當於 123456.789 * 10^5 */
b = a + 20 ;
/* 20加上無意義 */
printf(" %f ", b) ;
return 0;
}
編譯運行結果為:
為什麼計算出來不是正確的結果呢?是因為 float 數據的有效位是 7 位,a = 123456.789e5 這條語句中賦值給 a 的值超過了 float 的有效位;輸出結果中 12345678848.000000 中只有前 7 位才是有效數字
現在我們再次編寫 4-3.c,把程序修改如下:
#include <stdio.h>
int main(){
double a , b ; //把 float 改為 double 類型
a = 123456.789e5 ;
b = a + 20 ;
printf(" %f ", b) ;
return 0;
}
程序修改完成後保存並再次編譯,運行程序得到了以下結果:
這個時候我們發現運行的結果是一個正確的計算結果。因為 double 型的有效數字是 15~16 位,而 123456.789e5(12345678900)其有效數字是 11 位,把它賦值給 a 不會出現溢出。
在我們以後的項目編程中一定要小心數值溢出的問題,不要以為這種情況很難出現。舉個簡單的例子,我國很多城市的地鐵造價每公裡超過了 5 億,我國 2014 年上半年國內生產總值 269044 億元,這個時候如果我們定義變量為 float 甚至是 double 類型,都是很危險的。
C 語言中,字符型的基本類型符是 char。
上一節中我們講到了字符常量,字符型常量是用單引號括起來的一個字符。如 'A','a','?' 等等。在所有的編譯系統中都規定以 1 個字節(8 位)來存放一個字符。字符型數據在存儲時,並不是把該字符本身存放到內存單元中,而是把該字符相應的 ASCII 碼值存放到該存儲單元中。(什麼是 ASCII 碼,為什麼採用 ASCII 來存儲,大家可以查看百度百科 ASCII) 如 x 的十進位 ASCII 碼是 120,y 的十進位 ASCII 碼是 121。對字符變量 a、b 賦予 'x' 和 'y' 值:
實際上是在 a、b 兩個單元內存放 120 和 121 的二進位代碼:
另外還有需要注意的是,字符常量是區分大小寫的,例如,字符 'c' 的 ASCII 碼值是 99,'C' 的 ASCII 碼值是 67,兩者並不是同一個字符。大家可以通過 ASCII 碼錶查看字符對應的 ASCII 碼值。
我們編寫程序 4-4.c,代碼如下:
#include<stdio.h>
int main(){
char a,b; //定義a和b為字符型變量
a = 'c'; //把字符常量 'c' 賦值給變量 a
b = 121;
printf("%c,%c\n",a,b); //%c 表示以字符的形式輸出
printf("%d,%d\n",a,b); //%d 表示以有符號十進位形式輸出整數型
return 0;
}
編譯並運行之後的結果是:
本程序中定義 a,b 為字符型,在賦值語句中給 a 賦以字符值,但是給 b 卻賦以整型值。從結果看,a,b 值的輸出形式取決於 printf 函數格式串中的格式符,當格式符為 'c' 時,對應輸出的變量值為字符,當格式符為 'd' 時,對應輸出的變量值為整數。
也就是說,一個在字符的數據既可以以字符的形式輸出,也可以以整數的形式輸出。以字符形式輸出時,先將存儲單元中的 ASCII 碼轉換成相應的字符再輸出;以整數的形式輸出時,直接輸出其 ASCII 碼。
我們還可以對字符型數據進行算術運算,此時相當於對他們的 ASCII 碼進行運算,編寫程序 4-5.c:
#include<stdio.h>
int main(){
char a,x;
int b;
a = 'c';
b = 1;
x = a + b;
printf("%c\n",x);
printf("%d\n",x);
return 0;
}
編譯運行的結果是:
字符串常量是用一對雙引號括起來的零個或多個字符組成的序列,如 "hello","China","b" 都是字符串常量。
字符串常量的存儲與字符常量的存儲是不同的。字符串中的每個字符佔用一個字節,在存儲字符串常量時還要自動在其末尾加上 '\0' 作為字符串結束的標誌。我們先來一起看下 "How do you do." 是如何存儲的吧!
因此大家不要將字符常量和字符串常量混淆哦,'b' 和 "b" 是完全不同的。前者是字符常量,在內存中佔用的字節數為 1;而後者是字符串常量,在內存中佔用的字節數為 2,包含字符 『b』 和 『\0』。
注意:在 C 語言中沒有專門的字符串變量,如果你想要將一個字符串存放在變量中,必須使用字符數組,數組中每一個元素存放一個字符,數組的內容我們會在以後的課程中和大家詳細講述。
各位小夥伴,我們學到了字符串常量,這個時候有木有想起我們編寫的第一個程序 Hello world!回憶起我們一起走過的日子真好!
課後練習
習題一:計算距離追風少年小明騎電瓶車的速度是 40km/h,他以這樣的速度從家到公司花費了 1 小時 30 分鐘,紅綠燈時間忽略不計,小明家與公司的距離是多遠?
程序中變量的命名需要採用駱駝命名法。
習題二:製作中文圖書管理系統的登錄界面問題分析:中文圖書管理系統登錄界面是進入圖書管理系統的第一個界面,實現實現用戶登錄系統的功能,可據此對用戶的合法性進行檢查,本案例只是實現簡單的登錄功能。
用戶輸入:借書卡號,用戶姓名 期望的輸出:登錄成功的提示信息
.
有些程式設計師喜歡全部小寫,有些程式設計師喜歡用下劃線,所以如果要寫一個my name的變量,他們常用的寫法會有myname、my_name、MyName或者myName。這樣的命名規則不適合所有程式設計師閱讀,而利用駝峰命名法來表示,可以增加程序可讀性。例如,下面是分別用駱駝式命名法和下劃線法命名的同一個函數:printEmployeePaychecks();print_employee_paychecks();第一個函數名使用了駱駝式命名法——函數名中的每一個邏輯斷點都有一個大寫字母來標記;第二個函數名使用了下劃線法----函數名中的每一個邏輯斷點都有一個下劃線來標記。駱駝式命名法在許多新的函數庫和Microsoft Windows這樣的環境中使用得相當多。另一方面,下劃線法是c出現後開始流行起來的,在許多舊的程序和UNIX這樣的環境中,它的使用非常普遍。.
.
.
.
.
.
.
.
.
.
.
.
參考答案
習題一
#include <stdio.h>
int main(){
int speed = 40;
double time = 1.5;
double len;
len = speed * time;
printf("小明家與公司的距離是%.2f公裡\n",len);
return 0;
}
習題二
#include<stdio.h>
int main(){
int cardnum;
char name[20]; //小夥伴注意了,這個就是定義了一個字符串數組,可以容納 20 字符
printf("**********************************************************\n");
printf("********Welcome to the books management system************\n");
printf("**********************************************************\n");
printf("~~~~~~~~~\t\t\t\t~~~~~~~~~~~~~~\n");
printf("Please input your card number:\n");
scanf("%d",&cardnum);
printf("Please input your name:\n");
scanf("%s",name);
printf("\nWelcome,%s!Your card number is:%d\n",name,cardnum);
return 0;
}
結束啦~~
<GMORAKEL運維中心>