【零基礎學習iOS開發】【02-C語言】07-基礎資料型別 (Elementary Data Type)

來源:互聯網
上載者:User

標籤:

本文目錄
  • 一、取值範圍
  • 二、char
  • 三、說明符
  • 四、自動型別提升
  • 五、強制類型轉換

C語言有豐富的資料類型,因此它很適合用來編寫資料庫,如DB2、Oracle等大型資料庫都是C語言寫的。其中,提供了4種最常用的基礎資料型別 (Elementary Data Type):char、int、float、double,使用這些資料類型,我們就可以定義相應的變數來儲存資料。這講就來深入研究一下基礎資料型別 (Elementary Data Type)的一些使用細節。

回到頂部一、取值範圍

我們已經知道,不同資料類型所佔的儲存空間是不一樣的。比如在64bit編譯器環境下,char類型佔用1個位元組,int類型佔用4個位元組。位元組長度不一樣,包含的二進位位元就不一樣,能表示的資料範圍也就不一樣。因此,int類型能表示的資料範圍肯定比char類型大。下面來簡單算算64bit編譯器環境下int類型的取值範圍。

1.推算int類型的取值範圍

int類型佔用4個位元組,所以一共32位,那麼按理來說,取值範圍應該是:0000 0000 0000 0000 0000 0000 0000 0000~1111 1111 1111 1111 1111 1111 1111 1111,也就是10進位的0 ~ 232 - 1。但是int類型是有正負之分的,包括了正數和負數,那怎麼表示負數呢?就是拿最高位來當符號位,當最高位為0就是正數,最高位為1則是負數。即:1000 0000 1001 1011 1000 0000 1001 1011就是一個負數,0000 1001 0000 1101 0000 1001 0000 1101是一個正數。由於最高位是0才代表正數,因此最大的正數是0111 1111 1111 1111 1111 1111 1111 1111,也就是231 - 1。而最小的負數就是1000 0000 0000 0000 0000 0000 0000 0000,也就是-231(為什麼是這個值呢?可以根據前面章節提到的負數的二進位形式,自己去換算一下,看看1000 0000 0000 0000 0000 0000 0000 0000是不是-231。算不出也不用去糾結,不影響寫代碼,知道有這麼一回事就完了)。因此,int類型的取值範圍是-231 ~ 231 - 1。

注意:這個推算過程是不用掌握的,大致知道過程就行了,而且這個結論也不用去記,大致知道範圍就行了。

 

2.各種資料類型的取值範圍

int類型的取值範圍已經會算了,那麼其他資料類型的取值範圍就能夠以此類推。

(註:float和double由於是小數,它們的儲存方式是特別不一樣的,所以它們取值範圍的演算法也很不一樣,這裡不做介紹,也不用去掌握。e38表示乘以10的38次方,e-38表示乘以10的負38次方。)

上面表格中列出的只是64bit編譯器環境下的情況。如果你的編譯器是16bit或者32bit,這些資料類型的取值範圍肯定是不一樣的。比如int類型,在16bit編譯器環境下是佔用2個位元組的,共16bit,所以int類型的取值範圍是:-215 ~ 215 - 1。

 

3.數值越界1> 例子示範

前面已經看到,每種資料類型都有自己的取值範圍。如果給一個變數賦值了一個超出取值範圍的數值,那後果會不堪設想。

1 #include <stdio.h>2 3 int main()4 {5     int c = 1024 * 1024 * 1024 * 4;6     7     printf("%d\n", c);8     return 0;9 }

 

我們都知道,int類型能儲存的最大值是231-1。在第5行給int類型的變數c賦值一個比231-1大的數值:232 (1024是210)

先看看在終端中的輸出結果:,可以看出輸出的值是0。

 

2> 結果分析

我們可以簡單分析一下為什麼將232賦值給變數c之後輸出的是0。232的 二進位形式是:1 0000 0000 0000 0000 0000 0000 0000 0000,一共有33位位元。變數c佔用了4個位元組,只能容納32位位元,而且記憶體定址是從大到小的,因此,變數c在記憶體中的儲存形式是0000 0000 0000 0000 0000 0000 0000 0000,也就是0,最前面的那個1就不屬於變數c的了。

 

3> 結論

可以發現,如果超出了變數的取值範圍,那麼將損失精度,得到“垃圾資料”(“垃圾資料”就是指並非我們想要的資料)。可是,有時候我們確實要儲存一個很大很大的整數,比231-1還大的整數,這該怎麼辦呢?這個就要用到類型說明符,這這講的後面會討論。

 

回到頂部二、char1.簡單使用

char是C語言中比較靈活的一種資料類型,稱為“字元型”。既然叫“字元型”,那肯定是用來儲存字元的,因此我們可以將一個字元常量賦值給一個字元型變數。

1 #include <stdio.h>2 3 int main()4 {5     char c = ‘A‘;6     7     printf("%c\n", c);8     return 0;9 }

在第5行定義了一個char類型變數c,並將字元常量‘A‘賦值給了c。在第7行將字元變數c輸出到螢幕,%c的意思是以字元的格式輸出。

輸出結果:

 

2.字元常量一定要用單引號括住

1> 下面的寫法是錯誤的:

1 int main()2 {3     char c = A;4     return 0;5 }

編譯器會直接報第3行的錯,錯誤原因是:標識符A找不到。你直接寫個大寫A,編譯器會認為這個A是一個變數。因此寫成‘A‘才是正確的,或者在第3行代碼的前面再定義1個變數名叫做A的char類型變數。

2> 下面的寫法也是錯誤的:

1 int main()2 {3     char c = "A";4     return 0;5 }

第3行中的"A"並不是字元常量,而是字串常量,將字串"A"賦值給字元變數c是錯誤的做法。字串和字元的儲存機制不一樣,因此"A"和‘A‘是有本質區別的。

 

3.字元型變數還可以當做整型變數使用

1個字元型變數佔用1個位元組,共8位,因此取值範圍是-27~27-1。在這個範圍內,你完全可以將字元型變數當做整型變數來使用。

 1 #include <stdio.h> 2  3 int main() 4 { 5     char c1 = -10; 6      7     char c2 = 120; 8      9     printf("c1=%d  c2=%d \n", c1, c2);10     return 0;11 } 

由於第9行用的是%d,表示以十進位整數格式輸出,輸出結果:。因此,如果使用的整數不是很大的話,可以使用char代替int,這樣的話,更節省記憶體開銷。

 

4.字元型變數只能儲存單位元組字元

其實字元有2種類型:單位元組字元和雙位元組字元。

  • 單位元組字元:在記憶體中佔用1個位元組的字元。包括了26個英文字母的大小寫、10個阿拉伯數字等字元;
  • 雙位元組字元:在記憶體中佔用2個位元組的字元。包括了中國、日本和韓國等國家的文字,比如漢字。

1個字元型變數只佔用1個位元組,所以1個字元型變數只能儲存1個單位元組字元。

下面的寫法是錯誤的:

1 #include <stdio.h>2 3 int main()4 {5     char c = ‘ABCD‘;6     7     printf("%c\n", c);8     return 0;9 }

編譯器會對上面的代碼發出警告,並不會報錯,因此程式還是能夠運行。由於變數c只能儲存1個單位元組字元,最終變數c只儲存了‘ABCD‘中的‘D‘。

輸出結果:

 

5.字元型變數不能儲存漢字

在記憶體中,1個漢字需要用2個位元組來儲存,而1個字元型變數只佔用1個位元組的儲存空間,所以字元型變數不能用來儲存漢字。

下面的寫法是錯誤的:

1 int main()2 {3     char c = ‘男‘;    4     return 0;5 }

 編譯器會直接報第3行的錯誤。記住一個原則:單引號括住的必須是單位元組字元。

 

6.ASCII

說到字元,就不得不提ASCII這個概念

1> ASCII是基於拉丁字母的一套電腦編碼系統,是現今最通用的單位元組編碼系統,全稱是“American Standard Code for Information Interchange”。編碼系統,看起來好像很進階,其實就是一個字元集---字元的集合。

2> ASCII字元集包括了:所有的大寫和小寫英文字母,數字0到9,標點符號,以及一些特殊控制字元:如退格、刪除、製表、斷行符號,一共128個字元,全部都是“單位元組字元”。

3> 在電腦中的任何資料都是以二進位形式儲存的,因此每個ASCII字元在記憶體中是以二進位形式儲存的,而且只佔用1個位元組,位元的值就稱為這個ASCII字元的ASCII值。比如大寫字母A在記憶體中的二進位形式是:0100 0001,那麼它的ASCII值就是65。

4> 下面是一張ASCII碼字元表,ASCII碼值的範圍是0~127

5> 我們都知道1個char型變數只佔用1個位元組的儲存空間,而所有的ASCII字元都是單位元組字元,因此char型變數能儲存任何ASCII字元。而且在使用char型變數儲存ASCII字元時,可以直接用ASCII字元,也可以用ASCII值。

 1 #include <stdio.h> 2  3 int main() 4 { 5     char c1 = 65; 6      7     char c2 = ‘A‘; 8      9     printf("c1=%c  c2=%c \n", c1, c2);10     return 0;11 }

在第5、7行分別定義了字元型變數c1、c2。很明顯,變數c2儲存的是ACII字元‘A‘;變數c1儲存的是65,而ASCII值65對應的ASCII字元就是‘A‘,因此變數c1儲存的也是‘A‘。

由於第9行用的是%c,表示以字元格式設定輸出,輸出結果:

5> 經過上面的例子後,應該知道6和‘6‘的區別了吧

 1 #include <stdio.h> 2  3 int main() 4 { 5     char c1 = 6; 6      7     char c2 = ‘6‘; 8      9     printf("c1=%d  c2=%d \n", c1, c2);10     return 0;11 }

第5行給變數c1賦值了整數6,第7行給變數c2賦值了字元‘6‘,‘6‘的ASCII值是54。

由於第9行用的是%d,表示以十進位整數格式輸出,輸出結果:

 

回到頂部三、說明符1.什麼是說明符

1> 我們已經知道,在64bit編譯器環境下,1個int類型變數取值範圍是-231 ~ 231 - 1,最大值是231-1。有時候,我們要使用的整數可能比231-1還大,比如234這個整數,如果還堅持用int類型變數來儲存這個值的話,就會損失精度,得到的是垃圾資料。為瞭解決這個問題,C語言允許我們給int類型的變數加一些說明符,某些說明符可以增大int類型變數的長度,這樣的話,int類型變數能儲存的資料範圍就變大了。

2> C語言提供了以下4種說明符,4個都屬於關鍵字:

  • short  短型
  • long  長型
  • signed  有符號型
  • unsigned  無符號型

按照用途進行分類,short和long是一類,signed和unsigned是一類。

 

2.用法示範

這些說明符一般就是用來修飾int類型的,所以在使用時可以省略int

 1 // 下面兩種寫法是等價的 2  short int s1 = 1; 3  short s2 = 1; 4   5  // 下面兩種寫法是等價的 6  long int l1 = 2; 7  long l2 = 2; 8   9  // 可以連續使用2個long10  long long ll = 10;11  12  // 下面兩種寫法是等價的13  signed int si1 = 3;14  signed si2 = 3;15  16  // 下面兩種寫法是等價的17  unsigned int us1 = 4;18  unsigned us2 = 4;19  20  // 也可以同時使用2種修飾符21  signed short int ss = 5;22  unsigned long int ul = 5;

1> 第2行中的short int和第3行中的short是等價的。

2> 看第10行,可以連續使用兩個long。long的作用會在後面解釋。

3> 注意第21和22行,可以同時使用兩種不同的說明符。但是不能同時使用相同類型的修飾符,也就是說不能同時使用short和long 或者 不能同時使用signed和unsigned。

 

3.short和long

1> short和long可以提供不同長度的整型數,也就是可以改變整型數的取值範圍。在64bit編譯器環境下,int佔用4個位元組(32bit),取值範圍是-231~231-1;short佔用2個位元組(16bit),取值範圍是-215~215-1;long佔用8個位元組(64bit),取值範圍是-263~263-1

2> 總結一下:在64位編譯器環境下,short佔2個位元組(16位),int佔4個位元組(32位),long佔8個位元組(64位)。因此,如果使用的整數不是很大的話,可以使用short代替int,這樣的話,更節省記憶體開銷。

3> 世界上的編譯器林林總總,不同編譯器環境下,int、short、long的取值範圍和佔用的長度又是不一樣的。比如在16bit編譯器環境下,long只佔用4個位元組。不過幸運的是,ANSI \ ISO制定了以下規則:

  • short跟int至少為16位(2位元組)
  • long至少為32位(4位元組)
  • short的長度不能大於int,int的長度不能大於long
  • char一定為為8位(1位元組),畢竟char是我們編程能用的最小資料類型

4> 可以連續使用2個long,也就是long long。一般來說,long long的範圍是不小於long的,比如在32bit編譯器環境下,long long佔用8個位元組,long佔用4個位元組。不過在64bit編譯器環境下,long long跟long是一樣的,都佔用8個位元組。

5> 還有一點要明確的是:short int等價於short,long int等價於long,long long int等價於long long

 

4.long的使用注意1> 常量

long和int都能夠儲存整型常量,為了區分long和int,一般會在整型常量後面加個小寫字母l,比如100l,表示long類型的常量。如果是long long類型呢,就加2個l,比如100ll。如果什麼都不加,就是int類型的常量。因此,100是int類型的常量,100l是long類型的常量,100ll是long long類型的常量。

 1 int main() 2 { 3     int a = 100; 4      5     long b = 100l; 6      7     long long c = 100ll; 8      9     return 0;10 } 

變數a、b、c最終儲存的值其實都是100,只不過佔用的位元組不相同,變數a用4個位元組來儲存100,變數b、c則用8個位元組來儲存100。

其實,你直接將100賦值給long類型的變數也是沒問題的,照樣使用。因為100是個int類型的常量,只要有4個位元組,就能儲存它,而long類型的變數b有8個位元組,那肯定可以裝下100啦。

1 int main()2 {3     long b = 100;4     5     return 0;6 }

 

2> 輸出
1 #include <stdio.h>2 3 int main()4 {5     long a = 100000000000l;6     7     printf("%d\n", a);8     return 0;9 }

在第5行定義了long類型變數a,在第7行嘗試輸出a的值。注意了,這裡用的是%d,表示以十進位整數格式輸出,%d會把a當做int類型來輸出,它認為a是4個位元組的。由於a是long類型的,佔用8個位元組,但是輸出a的時候,只會取其中4個位元組的內容進行輸出,所以輸出結果是:

又是傳說的垃圾資料

 

那怎樣才能完整地輸出long類型呢?應該用格式符%ld

1 #include <stdio.h>2 3 int main()4 {5     long a = 100000000000l;6     7     printf("%ld\n", a);8     return 0;9 }

注意第7行,雙引號裡面的是%ld,表示輸出1個long類型的整數,這時候的輸出結果是:

 

如果是long long類型,應該用%lld

1 #include <stdio.h>2 3 int main()4 {5     long long a = 100000000000ll;6     7     printf("%lld\n", a);8     return 0;9 }

 

5.signed和unsigned

1> 首先要明確的:signed int等價於signed,unsigned int等價於unsigned

2> signed和unsigned的區別就是它們的最高位是否要當做符號位,並不會像short和long那樣改變資料的長度,即所佔的位元組數。

  • signed:表示有符號,也就是說最高位要當做符號位,所以包括正數、負數和0。其實int的最高位本來就是符號位,已經包括了正負數和0了,因此signed和int是一樣的,signed等價於signed int,也等價於int。signed的取值範圍是-231 ~ 231- 1
  • unsigned:表示無符號,也就是說最高位並不當做符號位,所以不包括負數。在64bit編譯器環境下面,int佔用4個位元組(32bit),因此unsigned的取值範圍是:0000 0000 0000 0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111 1111 1111 1111,也就是0 ~ 232 - 1

 

6.signed、unsigned也可以修飾char,long還可以修飾double

知道有這麼一回事就行了。

1 unsigned char c1 = 10;2 signed char c2 = -10;3 4 long double d1 = 12.0;

 

7.不同資料類型所佔用的儲存空間

 

回到頂部四、自動型別提升1.什麼是自動型別提升

先來看看下面的一則運算

 1 #include <stdio.h> 2  3 int main() 4 { 5     int a = 10; 6      7     double d = a + 9.5; 8      9     printf("%f \n", d);10     11     return 0;12 }

1> 在第5行定義了一個int類型的變數a,賦值了一個整數10。

2> 接著在第7行取出a的值10,加上浮點數9.5,這裡做了一個“加法運算”,並且將“和”賦值給d。所以d的值應該是19.5。

3> 在第9行使用格式符%f輸出浮點型變數d,預設是保留6位小數的。輸出結果為:

4> 看似這麼簡單的運算,其實包含了一些文法細節在裡面。嚴格來說,相同資料類型的值才能進行運算(比如加法運算),而且運算結果依然是同一種資料類型。第7行的情況是:變數a的值10是int類型(4位元組),9.5是double類型(8位元組)。很明顯,10和9.5並不是相同資料類型。按理來說,10和9.5是不允許進行加法運算的。但是,系統會自動對佔用記憶體較少的類型做一個“自動型別提升”的操作,也就把10提升為double類型。也就是說,本來是用4個位元組來存放10的,現在改為用8個位元組來存放10。因此,10和9.5現在都是用8個位元組來存放的,都是double類型,然後就可以進行運算了。並且把運算結果賦值給double類型的變數d。

5> 需要注意的是:經過第7行代碼後,變數a一直都還是int類型的,並沒有變成double類型。1個變數在它定義的時候是什麼類型,那麼就一直都是什麼類型。“自動型別提升”只是在運算過程中進行的。

 

2.常見的自動型別提升
 1 int main() 2 { 3     float a = 10 + 3.45f;// int 提升為 float 4      5     int b = ‘A‘ + 32; // char 提升為 int 6      7     double c = 10.3f + 5.7; // float 提升為 double 8      9     return 0;10 }

1> 注意第5行,系統會將字元‘A‘提升為int類型資料,也就是轉為‘A‘的ASCII值後再跟32進行加法運算。‘A‘的ASCII值是65,因此變數b的值為65+32=97。

2> 這個自動型別提升,知道有這麼一回事就行了,不用死記這規則,因為系統會自動執行這個操作。

 

回到頂部五、強制類型轉換1.什麼是強制類型轉換

先來看看下面的代碼

1 #include <stdio.h>2 3 int main()4 {5     int i = 10.7;6     7     printf("%d \n", i);8     return 0;9 }

1> 注意第5行,我們將一個8個位元組的浮點數10.7賦值給了只有4個位元組儲存空間的整型變數i。可以想象得到,把8個位元組的內容塞給4個位元組,肯定會損失精度。在第7行將變數i的值輸出,輸出結果是:

輸出值為10,這是必然的。

2> 這裡面也有一點文法細節,其實第5行做了一個“強制類型轉換”的操作:由於左邊是int類型的變數i,那麼就會強制把double類型的10.7轉換為int類型的10,並且把轉換後的值賦值給了整型變數i。由於C語言是文法限制不嚴格,所以系統會自動強制轉換,如果換做是其他文法嚴格的語言,比如Java,第5行代碼早就報錯了。

3> 如果寫得嚴格一點,明顯地進行“強制類型轉換”,應該這樣寫:

1 #include <stdio.h>2 3 int main()4 {5     int i = (int) 10.7;6     7     printf("%d \n", i);8     return 0;9 }

注意第5行,在10.7的前面加了個(int),表示強制轉換為int類型的資料。這樣就絕對不會有文法問題了。總之你將一個浮點型資料轉換為整型資料,就會丟失小數部分的值。

 

2.常見的強制類型轉換
 1 int main() 2 { 3     int a = 198l; // long 轉換為 int 4      5     char b = 65; // int 轉換為 char 6      7     int c = 19.5f; // float 轉換為 int 8      9     return 0;10 }

這個強制類型轉換,知道有這麼一回事就行了,不用死記這規則,因為很多時候系統會自動執行這個操作。

 

3.其他用法

前面看到的強制轉換好像都是“大類型”轉為“小類型”,其實這是不一樣的,也可以由“小類型”轉為“大類型”

1 int main()2 {3     int a = 10;4     5     double b = (double)a  + 9.6;6     7     return 0;8 }

注意第5行,先將a的值強制轉換為double類型後,再跟9.6進行加法運算。這樣的話,系統就不用執行“自動型別提升”的操作了。其實你不強轉也可以的,因為系統會做一個“自動型別提升”的操作,將變數a的值10提升為double類型。知道有這用法就行了,以後某些地方會用得上。

 

【零基礎學習iOS開發】【02-C語言】07-基礎資料型別 (Elementary Data Type)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.