不能再詳細的C++基本數據類型剖析

前言

C++作爲一門強類型的編程語言,每個變量都有明確的所屬類型,變量類型決定了其能夠參與的運算、函數參數的傳遞、變量賦值等一系列操作。

本篇博客主要詳細剖析C++中的基本數據類型相關用法和注意事項,掃除大家在理解和使用C++中基本數據類型相關的障礙和困惑。博客所記錄可能不是非常全,但求基本覆蓋常見用法。

C++基本數據類型簡介

下表列出了幾種基本的 C++ 數據類型:

類型 關鍵字
布爾型 bool
字符型 char
整型 int
浮點型 float
雙浮點型 double

一些基本類型可以使用一個或多個類型修飾符進行修飾:
signed
unsigned
short
long

常用類型驗證相關操作

在進行後續詳細講解前,此處我先介紹自己在驗證這些類型運算所用到的一些輔助函數和功能,正是有了這些功能函數和功能類的幫助,我們才能更好的研究這些數據類型。

1、C++變量類型打印函數typeinfo
如果你熟悉Python的話你肯定用到過Python中的type函數,其可以方便獲取變量的類型。從C++11以後,也引入了一個typeinfo函數,可以用來打印基本數據類型變量所屬類型。這對我們驗證和調試一些變量的類型非常方便,這一節首先來看下如何使用typeinfo來打印數據類型。

直接上代碼:

#include <typeinfo>
#include <iostream>
#include <cmath>

using std::cout;
using std::endl;

void UseTypeInfo() {
    cout << "=========Begin test UseTypeInfo=========" << endl;
    bool bool_var;
    char char_var;
    int int_var;
    unsigned int uint_var;
    long long_var;
    unsigned long ulong_var;
    long long longlong_var;
    unsigned long long ulonglong_var;
    float float_var;
    double double_var;

    cout<<typeid(bool_var).name()<<endl;
    cout<<typeid(char_var).name()<<endl;
    cout<<typeid(int_var).name()<<endl;
    cout<<typeid(uint_var).name()<<endl;
    cout<<typeid(long_var).name()<<endl;
    cout<<typeid(ulong_var).name()<<endl;
    cout<<typeid(longlong_var).name()<<endl;
    cout<<typeid(ulonglong_var).name()<<endl;
    cout<<typeid(float_var).name()<<endl;
    cout<<typeid(double_var).name()<<endl;
    cout << "=========End test UseTypeInfo=========" << endl;
}

int main() {
    UseTypeInfo();
    return 0;
}

生成可執行文件basic_data_type,執行:./basic_data_type後結果爲:

=========Begin test UseTypeInfo=========
b
c
i
j
l
m
x
y
f
d
=========End test UseTypeInfo=========

這是什麼意思呢?其實是每個基本數據類型的縮寫,具體含義爲:

bool:                     b
char:                     c
signed char:              a
unsigned char:            h
(signed) short (int):     s
unsigned short (int):     t
(signed) (int):           i
unsigned (int):           j
(signed) long (int):      l
unsigned long (int):      m
(signed) long long (int): x
unsigned long long (int): y
float:                    f
double:                   d
long double:              e

如果你覺得這些縮寫看起來比較彆扭的話,你可以在執行可執行文件命令後面加上c++filt -t,即執行:./basic_data_type | c++filt -t,就可以得到如下打印結果:參考

=========Begin test UseTypeInfo=========
bool
char
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double
=========End test UseTypeInfo=========

2、獲取類型佔用字節數函數sizeof
在驗證C++基本數據類型一些操作時,另一個很重要的函數就是sizeof,其可以獲取一個類型所佔用內存的字節數:

void UseSizeOf() {
    cout << "=========Begin test UseSizeOf=========" << endl;
    cout<<"bool byte size: "<<sizeof(bool)<<endl;
    cout<<"char byte size: "<<sizeof(char)<<endl;
    cout<<"short int byte size: "<<sizeof(short int)<<endl;
    cout<<"unsigned short int byte size: "<<sizeof(unsigned short int)<<endl;
    cout<<"int byte size: "<<sizeof(int)<<endl;
    cout<<"unsigned int byte size: "<<sizeof(unsigned int)<<endl;
    cout<<"long int byte size: "<<sizeof(long int)<<endl;
    cout<<"long long int byte size: "<<sizeof(long long int)<<endl;
    cout<<"float byte size: "<<sizeof(float)<<endl;
    cout<<"double byte size: "<<sizeof(double)<<endl;
    cout << "=========Begin test UseSizeOf=========" << endl;
}

執行結果爲:

=========Begin test UseSizeOf=========
bool byte size: 1
char byte size: 1
short int byte size: 2
unsigned short int byte size: 2
int byte size: 4
unsigned int byte size: 4
long int byte size: 8
long long int byte size: 8
float byte size: 4
double byte size: 8
=========Begin test UseSizeOf=========

要注意一個字節爲8位(8 bit),並且不同類型佔用字節大小會根據編譯器和所使用的電腦而有所不同,通過類型佔用字節大小和其有符號和無符號的區別,我們就可以計算出該類型所能表示的最大最小的數值,當然我們也可以用下面介紹的std::numeric_limits模板類提供的函數直接獲取不同類型的極值。

3、獲取每個類型所能表示的數值範圍
C++中由於每個類型所能表示的數值範圍並不相同,不同數值類型不能直接參與運算,需要進行類型轉換,而在無符號、有符號類型之間進行轉換時會發生各種不可意料的事情,因此瞭解每種基本數據類型所能表示的最大最小範圍很重要。

C++中提供了std::numeric_limits模板類以及頭文件cfloat中的DBL_MINDBL_MAX等函數可以用來方便獲取不同數據類型所能表示的數值範圍:

#include <limits>
#include <cfloat>

void UseNumericalLimit() {
    cout << "=========Begin test UseNumericalLimit=========" << endl;
    cout<<"uint16_t min: "<< std::numeric_limits<uint16_t>::min()<<endl;
    cout<<"uint16_t max: "<< std::numeric_limits<uint16_t>::max()<<endl;

    cout<<"short min: "<< std::numeric_limits<short>::min()<<endl;
    cout<<"short max: "<< std::numeric_limits<short>::max()<<endl;

    int int_min = std::numeric_limits<int>::min();
    cout<<"int min: "<<int_min<<endl;
    int int_max = std::numeric_limits<int>::max();
    cout<<"int max: "<<int_max<<endl;

    unsigned int unsigned_int_min = std::numeric_limits<unsigned int>::min();
    cout<<"unsigned int min: "<<unsigned_int_min<<endl;
    unsigned int unsigned_int_max = std::numeric_limits<unsigned int>::max();
    cout<<"unsigned int max: "<<unsigned_int_max<<endl;

    float float_min = std::numeric_limits<float>::min();
    cout<<"float min: "<<float_min<<endl;
    float float_max = std::numeric_limits<float>::max();
    cout<<"float max: "<<float_max<<endl;

    double double_min = std::numeric_limits<double>::min();
    cout<<"double min: "<<double_min<<endl;
    double double_max = std::numeric_limits<double>::max();
    cout<<"double max: "<<double_max<<endl;

    double_min = DBL_MIN;
    cout<<double_min<<endl;
    double_max = DBL_MAX;
    cout<<double_max<<endl;
    
    cout << "=========Begin test UseNumericalLimit=========" << endl;
}

執行結果如下:

=========Begin test UseNumericalLimit=========
uint16_t min: 0
uint16_t max: 65535
short min: -32768
short max: 32767
int min: -2147483648
int max: 2147483647
unsigned int min: 0
unsigned int max: 4294967295
float min: 1.17549e-38
float max: 3.40282e+38
double min: 2.22507e-308
double max: 1.79769e+308
2.22507e-308
1.79769e+308
=========Begin test UseNumericalLimit=========
int main()
{
    auto a = 5u - 2147483648;
    unsigned int b = 10;
    cout<<typeid(a).name()<<" "<<typeid(b).name()<<endl;
    cout<<a<<endl;

    cout<<sizeof(int)<<" "<<sizeof(long)<<" "<<sizeof(long long)<<endl;
    cout<<std::numeric_limits<long>::min()<<endl;
    cout<<std::numeric_limits<int>::max()<<endl;
    cout<<std::numeric_limits<unsigned int>::max()<<endl;
}

基本數據類型使用細節

數值常量的類型

所謂常量,就是在整個程序運行過程中始終不變的量,簡單來說,就是程序中直接使用的數值、字符和字符串等,C++中的常量主要包括整型常量、浮點型常量、字符常量和字符串常量,此處主要講解整型和浮點型兩種數值常量。

整型常量

整數常量可以是十進制、八進制或十六進制的常量。前綴指定基數:0x 或 0X 表示十六進制,0 表示八進制,不帶前綴則默認表示十進制。

整數常量也可以帶一個後綴,後綴是 U 和 L 的組合,U 表示無符號整數(unsigned),L 表示長整數(long)。後綴可以是大寫,也可以是小寫,U 和 L 的順序任意。

一些簡單示例:

85         // 十進制
0213       // 八進制 
0x4b       // 十六進制 
30         // 整數 
30u        // 無符號整數 
30l        // 長整數 
30ul       // 無符號長整數

浮點常量

浮點常量由整數部分、小數點、小數部分和指數部分組成,你可以使用小數形式或者指數形式來表示浮點常量。根據取值範圍的不同,C++中的浮點型數值類型可以分爲單精度型float、雙精度型double和長雙精度型long double。

當使用小數形式表示時,必須包含整數部分、小數部分,或同時包含兩者。當使用指數形式表示時, 必須包含小數點、指數,或同時包含兩者。帶符號的指數是用eE引入的。

下面列舉幾個浮點常量的實例:

3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指數
210f          // 非法的:沒有小數或指數
.e55          // 非法的:缺少整數或分數

爲了詳細瞭解浮點數據,我們需要知道C++中浮點類型數據的內存存儲結構,參考

1、float類型
float類型佔四個字節,每個字節佔8位,總共32位,其內存結構如下圖:
在這裏插入圖片描述
可以看出,float類型包含有:1位符號位、8位指數位和23位尾數位。

由於指數可正可負,因此採用移位存儲的8位指數位表示的範圍爲-128~+128,其中負指數決定了浮點數所能表達的絕對值最小的非零數;而正指數決定了浮點數所能表達的絕對值最大的數,也即決定了浮點數的取值範圍。

因此float的範圍爲-2^128 ~ +2^128,也即-3.40282E+38 ~ +3.40282E+38,這也與我們前面使用std::numeric_limits得到的值保持一致。
2、double類型
double類型內存中存儲的原理與float類型是一樣的,只不過double類型是用64位來表示的,其存儲結構如下:
在這裏插入圖片描述
可以看出,double類型包含有:1位符號位、11位指數位和52位尾數位。

類似的,double類型的取值範圍爲-2^1024~+2^1024,也即-1.79769e+308~+1.79769e+308

參考:
C/C++中float & double類型數據在內存中的存儲形式
浮點數表示方法與精度:
https://www.learncpp.com/cpp-tutorial/floating-point-numbers/
內存模型:
https://blog.csdn.net/whzhaochao/article/details/12885875
https://blog.csdn.net/whzhaochao/article/details/12887779

數值常量默認類型

C++中每個數據都是有類型的,數值常量也不例外,對於一個常數來說,其也有默認的數據類型,也可以通過上述後綴來改變具體類型。

下面示例代碼我們使用前面提到的typeinfo函數來獲取數值常量的類型:

int main() {
    auto x = -10U;
    cout<<x<<endl;

    cout<<typeid(0).name()<<endl;
    cout<<typeid(-10U).name()<<endl;
    cout<<typeid(2147483647).name()<<endl; # int類型最大值爲2147483647
    cout<<typeid(2147483648).name()<<endl;
    cout<<"10L: "<<typeid(10L).name()<<endl;
    cout<<"10U: "<<typeid(10U).name()<<endl;

    cout<<typeid(10.0).name()<<endl;
    cout<<typeid(10.0F).name()<<endl;
}

執行結果爲:

0: int
-10: int
2147483647: int
2147483648: long
10L: long
10U: unsigned int
10.0: double
10.0F: float

從輸出結果可以看出幾個關鍵點:
1、整型常量默認爲有符號int類型,如果整型常量大小超出int,就會轉變爲long int類型,如果需要無符號整形常量的話就可以在常量後面加u或是U,如0u或是0U,同理可以在常量後面加ul或UL表示無符號長整形常量;

2、浮點型默認爲double,想要單精度float類型數據,需要加後綴f或F;

3、浮點型數據沒有u或U,因爲浮點數一般都爲有符號

基本數據類型運算時的類型轉換問題

有符號類型與無符號類型的迷茫:
https://www.jianshu.com/p/cd30cbb78f4f
https://blog.csdn.net/fhyangchina/article/details/55250934

隱式類型轉換:
https://www.cnblogs.com/QG-whz/p/4472566.html
https://www.cnblogs.com/predator-wang/p/5230902.html
https://zhuanlan.zhihu.com/p/81588535
https://en.cppreference.com/w/c/language/conversion

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章