程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 你好,C++(9)坐216路公交車去買3.5元一斤的西紅柿——C++中如何表達各種數值數據 3.3 數值數據類型,216路3.3

你好,C++(9)坐216路公交車去買3.5元一斤的西紅柿——C++中如何表達各種數值數據 3.3 數值數據類型,216路3.3

編輯:C++入門知識

你好,C++(9)坐216路公交車去買3.5元一斤的西紅柿——C++中如何表達各種數值數據 3.3 數值數據類型,216路3.3


3.3  數值數據類型

從每天早上睜開眼睛的那一刻開始,我們幾乎每時每刻都在與數字打交道:從鬧鐘上的6點30分,到上班坐的216路公共汽車;從新聞中說的房價跌到了100元每平米到回家買菜時的西紅柿3.5元一斤。我們生活在一個充滿數字的世界。程序是對現實世界的描述與表達,自然也會有很多數字需要表達。為了做到這一點,C++提供了豐富多樣的數值數據類型,從整數到小數、從單精度浮點數到雙精度浮點數、從有符號數到無符號數。有了這些數值數據類型,我們就可以定義各種變量來表示現實世界中的各種數字了。

3.3.1  整型數值類型

在現實世界中,最常見的數字應該是各種大大小小的整數了,而在C++中,我們用整型數值類型來表示現實世界中的整數。根據這些數據類型所占用的內存資源多少和取值范圍的不同,整型數值類型又被分為以下四種。

1. 基本型

其類型說明符為int(integer),在內存中占4個字節,其取值范圍從-2147483648到2147483647,基本上包含了我們最常見的整數,能夠滿足我們在程序中對表達整數的需要。同時,又因為它是CPU原生支持的整型類型,所以處理起來最快。因此,int是我們最為常用的整型數值類型。

特別注意:數據類型的字節數在不同環境下可能有不同值

需要注意的是,我們在這裡所討論的各種數據類型所占用的內存字節數,只是在典型情況(32位操作系統,32位主流編譯器)下的字節數。在一些特殊環境(64位操作系統、特殊的編譯器)下,各種數據類型所占用的字節數可能有所不同。所以,要想獲得某種數據類型在當前環境下所占用的字節數,最好的方法是使用sizeof關鍵字動態地計算其實際的字節數,而不是使用某個固定的字節數。具體方法,可以參看後文對sizeof的介紹。

2. 短整型

有時候,我們需要表達的整數只是在一個比較小的范圍內,比如,我們要表達一個學生的成績,最小可以到0,而最大也只能到100,如果這時仍舊使用int這種取值范圍比較大的數據類型來表示,就顯得有點資源浪費了。現在正在倡導創建節約型社會,而C++世界也不例外。為了表示這種取值范圍相對較小的整數,C++提供了短整型數據類型,其類型說明符為short或short int,所占內存字節數只有int類型的一半,也就是兩個字節。自然地,其取值范圍也相應地縮小為從- 32768到32767,我們可以用它來表達生活中常見的整型數值。但是,相對於int類型,short類型的處理速度要慢一些,而內存又相對比較便宜,所以,我們往往為了性能而犧牲空間,更多的使用int類型來代替short類型。

3. 長整型

在某些情況下,我們並不知道我們需要表達的整數到底有多大,有可能是幾千,也有可能是幾千個億。在這種情況下,我們總是希望得到一個當前平台支持的最大整型數值類型,以避免因取值范圍太小而造成的錯誤。在C++中,我們用長整型來表示當前平台原生支持的最大整型數值類型。其類型說明符為long或long int。在32位平台上,它占用4個字節的內存,其取值范圍與int相同。而在64位平台上,其占用的內存擴展為8個字節,取值范圍也相應地增大為駭人聽聞的從-9223372036854775808到9223372036854775807,絕對滿足我們對整數取值范圍的需要。

4. 長長整型

在C++中,為了表示現實世界中的某些特別大特別大的整數,比如某個星系的星球總數,我們還需要用到取值范圍更大的長長整型。它的類型說明符是long long或long long int,在內存中占8個字節。其取值范圍已經是個天文數字,所以它也只是更多地用於天文計算等特殊的科學計算中。

另外,還可以用關鍵字unsigned或者signed對這些整型數據類型進行修飾,構成無符號或有符號的整型數據類型。所謂的有無符號,是指這些數值在內存中的第一位是用來表示正負符號還是用來表示數值。在默認情況下,上面所介紹的整型數據類型都是有符號的,只要在其類型說明符之前加上unsiged關鍵字,就成為了對應的無符號整型數據類型。

int a;  // 默認為有符號int類型
signed short b;  // 有符號short類型
unsighed long c; // 無符號long類型

各種無符號類型所占用的內存空間與相應的有符號類型占用的內存空間是相同的。但由於無符號類型省去了符號位,故不能表示負數,而相應的,它所能夠表示的正數范圍會擴大到原來的一倍。當我們事先知道要表示的整數不會出現負數的時候,可以使用unsigned修飾某個整型數值類型,使其成為一個無符號的數據類型,擴展其在正數上的取值范圍。表3-3列出了在典型的32位平台上,C++中各種整型數值數據類型、取值范圍、位數和使用規則。

表3-3  整型數值類型

數   據 類 型

位數

取   值 范 圍

使   用 規 則

int

32

-2147483648 ~  2147483647

最常用的,也是處理速度最快的整型數值類型,用來表示常見的整型數值

signed   int

32

-2147483648 ~    2147483647

在使用上等同於int類型,所以很少使用,基本上都是直接使用int類型

unsigned   int

32

0 ~ 4294967295

無符號的int類型,只能表示正整數。當要表示的整數只有正數值的時候,可以使用無符號數據類型

short int

16

-32768 ~ 32767

通常簡寫為short,用以表示范圍較小的整數以節省內存資源,但處理速度有所減低

signed   short int

16

-32768 ~ 32767

unsigned   short int

16

0 ~ 65535

long int

32

-2147483648 ~ 2147483647

通常簡寫為long,它代表了當前平台上CPU原生支持的最大整型數據類型。在32位平台上,long和int的取值范圍是相同的,只是在64位操作系統上兩者有差別,這一點在開發跨平台的應用程序時需要注意

signed long   int

32

-2147483648 ~ 2147483647

unsigned long   int

32

0 ~ 4294967295

long long   int

64

-9223372036854775808 ~

9223372036854775807

比long還要long的整數,我們不用去數它到底有多少位,只需要知道,當要表示很大很大的整數時,就可以用它

 

知道更多:用sizeof牌體重計為數據類型量體重

在現實世界中的人們,無論高矮都有個體重,人們根據自己的體重來決定穿多大尺寸的衣服;而在C++世界中的各種數據類型,同樣也都有一個體重,而數據類型的體重,決定了它需要占用多少字節的內存空間。一個數據類型所占用的內存字節數,也就是它的體重。

為什麼我們需要知道數據類型的體重?在通過指針直接操作內存對數據進行讀寫訪問的時候,我們往往需要知道這種數據類型所占用的內存字節數,這樣我們才能為這些數據分配合適的內存資源,就像我們要為一個人做衣服,先要知道他的身高一樣。例如,我們想動態地申請一段內存來保存1024個long類型的數據,這時我們就需要根據每個long類型數據占用的內存字節數來計算一共需要申請多少字節的內存資源來存放這些數據。雖然我們知道long類型的字節數大多數時候是4個字節,但是,我們不能在程序中直接使用這個從天而降的魔數(Magic Number),那樣一個不知從何而來的數字會讓程序的可維護性變得很差;同時,數據類型占用的字節數就像人的體重一樣,是會根據環境的變化而發生變化的,並不是所有平台上的long類型都是4個字節。如果我們在程序中用固定的數字4來表示long數據類型的字節數,在某些平台上這個程序也許會運行正常,但當這個程序移植到另外一個平台時,將有可能因為long數據類型字節數的不同而產生錯誤。為了提高程序的可維護性,保證程序的可移植性,我們必須在代碼中動態地獲得數據類型在當前平台上的字節數,並最終計算出需要的內存資源總數。

所有這些關於數據類型字節數的問題,我們都可以使用C++提供給我們的sizeof牌數據類型體重計來一勞永逸地輕松解決。sizeof實際上是C++中的一個操作符,它可以對一個數據類型或者這個數據類型的一個變量或數組進行操作,從而獲得這個類型或者變量(數組)實際占用的內存字節數。例如,我們在用memset()函數清零某個數組時,通常用它來計算這個數組的字節數:

#include <cstring> // 引入memset()函數所在的頭文件

// …
// 定義數組
int res[1024];
// 用sizeof計算res占用的字節數
memset(res,0,sizeof(res));

這裡我們在用memset()函數對數組res進行清零操作時,第一個參數是數組名,也就是數組的首地址,第二個參數是初始值,通常是0,第三個參數就是用sizeof關鍵字計算出來的res數組所占用的字節數了。幸虧有sizeof這個體重計,要不然我們這裡要寫成更復雜的“1024*4”。這種復雜的形式雖然也同樣能夠達到計算數組體積的目的,但是,很容易出錯,且毫無可移植性可言。而這,也正是sizeof體重計受到程序員們鐘愛的原因。

sizeof牌數據類型體重計不僅可以獲得內建數據類型(例如,int、long和double等)的體重,它同樣也可以獲得自定義的結構體或者類所占用的字節數(包含因為字節對齊而添加的字節數)。例如:

// 定義一個結構體
struct Human
{
    char cName[3];   // 3個字節
    // char pad;      // 這裡因字節對齊,編譯器補齊一個字節
    int nAge;       // 4個字節
};

// 聲明一個結構體變量
Human huZengmei;
// 輸出結構體的字節數為8,包含因字節對齊而添加的一個字節,即3+1+4=8
cout<<"Human結構體占用的字節數是:"<<sizeof(Human)<<endl;
// 輸出結構體變量的字節數,與結構體的字節數相同
cout<<"Human對象占用的字節數是:"<<sizeof(huZengmei)<<endl;

sizeof牌體重計是如此好用,它應該應用在任何需要知道某個數據類型或者變量(包括數組)所占用內存空間的地方,例如,用memset()函數對數組進行清零操作,根據某個基本數據類型占用的字節數來判斷當前硬件平台是32位還是64位等,以此來避免人為地指定數據類型的字節數可能帶來的可維護性和可移植性問題。

                       

圖3-2 sizeof牌體重計

3.3.2  浮點型數值類型

現實生活中的數字,除了表示公共汽車路數的216這種整數外,更多地是表示西紅柿價格的3.5這種小數。而在C++中,我們使用浮點型數值類型來表示小數。根據取值范圍的不同,C++中的浮點型數值類型可以分為單精度型、雙精度型和長雙精度型三種。

1. 單精度型

其類型說明符為float。單精度浮點型數值類型占4個字節的內存空間,其取值范圍為-3.4E+38 ~ +3.4E+38。這裡需要注意的是,因為浮點型數值類型(包括後面的double和long double類型)無法精確地表示零值,所以其取值范圍實際上並不連續,在中間接近零值的地方,被分為了正負兩部分。因為受到計算機存儲浮點數機制的限制,使用float類型表示浮點數時,能夠保證精確到小數點前後至少6位有效數字,最多可以達到7位有效數字。例如:

float fPrice = 3.5;    // 用float類型的變量fPrice表示西紅柿3.5元一斤

知道更多:為什麼小數在C++中被稱為浮點數?

在C++中,我們將小數稱為浮點數,而將表示小數的數據類型稱為浮點型數值類型。這裡大家一定會問:為什麼小數被稱為浮點數?而其中的“浮”又是什麼意思呢?

這一切,都與小數在C++中的表達方式有關系。所謂的浮點,是相對於定點而言。比如,我們要在C++中表達這樣兩個小數:

100000000000.0
0.000000000001

如果采用定點(小數點固定)的表達方式,我們需要保存成如下的形式:

100000000000.000000000000
000000000000.000000000001

采用這種方式,我們不得不將每一位上的數據都原原本本地保存下來,這其中的某些數據對於小數的數值和精度都毫無意義,反而卻浪費了寶貴的存儲資源。為了解決這個問題,C++采用了一種新的保存方式:將數字表示成指數形式,保存每個數字的有效數字和指數。按照這種方式,上面的兩個數可以保存成如下的形式:

小數 1 指數  11(小數點往左移動了11位)
小數 1 指數 -12(小數點往右移動了12位)

通過小數點位置的移動,我們只需要保存小數的有效數字和小數點移動的位置,就可以以更加簡潔的方式保存下整個數字。因為這種表達方式中的小數點是浮動(float)的,所以小數也被稱為浮點數。  

2. 雙精度型

其類型說明符為double。雙精度浮點型數值類型占8 個字節的內存空間,是單精度浮點數值類型的兩(double)倍,所以雙精度類型不僅取值范圍更大,可以達到-1.7E+308~1.7E+308,同時其精度也更高,可以精確到小數點前後15位有效數字,最多可以達到16位。例如:

double fD = 0.0000003;   // 用double類型的變量表示支原體細胞的直徑

3. 長雙精度型

其類型說明符為long double。長雙精度浮點型數值類型占12個字節的內存空間,其數值范圍可以達到天文數字級別的-1.2E+4932~1.2E+4932所以,這種類型更多地只是用於科學計算中,日常開發較少用到。

表3-4列出了在典型的32位平台上,浮點型數值的數據類型、位數、精確數字位數、取值范圍和使用規則。

表3-4  浮點型數值類型

數 據 類 型

位數

有效數字

取 值 范 圍

使 用 規 則

float

32

6-7

-3.4E+38 ~ 3.4E+38

如果要表示的浮點數數值不是特別大,精度要求也不是很高,比如我們日常生活中遇到的各種小數,就可以使用float類型來表示,不僅可以滿足需要還可以節省內存空間,提高性能

double

64

15-16

-1.7E+308 ~ 1.7E+308

如果要表示的浮點數數值比較大,或精度要求比較高,可以使用double類型來表示,雖然占用了更多內存空間,但是可以保證取值范圍和數據精度

long double

96

18-19

-1.2E+4932 ~ 1.2E+4932

如果要表示天文數字,就用它

           

知道更多:如何產生隨機數?

所謂隨機數,通俗地講,就是由計算機通過一定的隨機數算法所產生的,按照某種規律分布(平均分布或正態分布)的某個大小范圍內的數字。在程序設計中,隨機數廣泛地被應用在測試、游戲、仿真以及安全等領域。所以,掌握各種隨機數的產生方式,也成為我們使用C++進行開發的一個必備技能。

在C++11中,一個隨機數的產生需要由隨機引擎(engine)對象和分布(distribution)對象兩部分共同完成。其中,分布對象負責隨機數的取值范圍和分布。比如,使用uniform_int_distribution分布,表示將引擎產生的隨機數字平均分布在某個范圍內;而使用normal_distribution分布,則表示將這些隨機數字正態分布在某個范圍。相應地,引擎對象則負責根據分布對象確定的取值范圍和分布產生相應的隨機數字。當我們在程序中確定隨機數產生所需要的引擎對象和分布對象後,就可以用引擎對象作為參數,調用分布對象這個函數對象,從而得到我們所需要的隨機數了。例如,網站登錄驗證碼的產生就需要用到隨機數:

// 引入隨機數引擎和分布所在的頭文件
#include <random>
#include <iostream>

// 使用std名字空間
using namespace std;

int main()
{
        // 定義一個默認的隨機數引擎
        default_random_engine reng; 
        // 構建一個從0到25之間的平均分布
        uniform_int_distribution<int>  uni_dist(0,25); 
        
        // 使用random_device設置隨機數引擎的種子,
        // 以防止每次運行都產生相同的偽隨機數序列
        random_device  rnd_device; 
        reng.seed(rnd_device());  

        // 驗證碼一共4位
        const int n = 4;    
        char code[n]; // 保存驗證碼的字符數組
        // 提示輸入驗證碼
        cout<<"請輸入下面的驗證碼:"<<endl;
        // 利用for循環產生4個驗證碼字母字符
        for (int i = 0; i < n; ++i)
        {
             // uni_dist(reng)表示讓reng引擎按照uni_dist分布,
             // 產生取值在0到25之間呈平均分布的隨機數
             // 然後在‘A’的基礎上向後偏移,就得到了隨機的驗證碼字母字符
             code[i] = 'A' + uni_dist(reng); 

             // 輸出驗證碼字母字符
             cout<<code[i];
         }

         // …
                    
         return 0;
}

在這段程序中,我們首先引入了C++標准庫中關於隨機數的頭文件<random>,然後,我們就可以定義相應的隨機數引擎對象(reng)和分布對象(uni_dist),在定義分布對象的同時,我們以構造函數參數的形式,確定了隨機數的取值范圍。有了它們,就可以用引擎對象reng作為參數,調用uni_dist分布對象這個函數對象,最後得到的就是我們需要的在0和25范圍之內平均分布的隨機數了。在這裡,我們還利用了ASCII表中字母字符呈現連續分布的特性,在字符‘A’的基礎上,加上一個隨機數,就得到了我們最終想要的隨機的字母字符。值得特別提醒的是,在產生隨機數之前,我們必須用引擎對象的seed()函數設置隨機種子,否則,每次運行所產生的隨機數序列都是一樣的,那樣就失去了隨機的意義了。

另外,這個程序只是利用隨機數產生了驗證碼字符,接下來,我們還需要接收用戶的輸入,並與當前的驗證碼進行比較,以此來判斷用戶輸入是否正確。這些工作,就留給大家在學習了後面的內容(各種控制結構、字符串處理等)之後,自己動手來完成了。相信大家很快就可以做到這一點。

------->

敬請期待下集:他到底愛不愛我?3.4 布爾類型




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved