C++變量和基本類型詳解

基本內置類型

算術類型分為兩類:整型(包括字符和佈爾類型在內)和浮點型

1. 不同平臺下基本類型的字節數

類型 16位平臺 32位平臺 64位平臺
char 1 1 1
short 2 2 2
int 2 4 4
long 4 4 8
long long / 8 8
指針 2 4 8
float 4 4 4
double 8 8 8

數據類型long long 是在C++11中新定義的。

2. 算數類型的最小尺寸

算術類型分為兩類:整型(包括字符和佈爾類型在內)和浮點型

類型 含義 最小尺寸
bool 佈爾類型 未定義
char 字符 8位
wchar_t 寬字符 16位
char16_t Unicode字符 16位
char32_t Unicode字符 32位
short 短整型 16位
int 整型 16位
long 長整型 32位
long long 長整型 64位
float 單精度浮點數 6位有效數字
double 雙精度浮點數 10位有效數字
long double 擴展雙精度浮點數 10位有效數字

3. 數據類型選擇的經驗準則

  • 當知曉數值不可能為負時,選用無符號類型
  • 使用int執行整數運算,short常常太小,long和int一般尺寸一樣。如果int不夠,用long long。
  • 在算數表達式中不要使用char或bool,使用char特別容易出問題。如果要使用一個不大的整數,那麼明確執行為signed char或unsigned char。
  • 執行浮點數運算用double,float通常進度不夠而且兩者計算代價相差無幾。Long double一般沒有必要,且消耗不容忽視。

4. 有符號類型和無符號類型

  • 無符號類型賦值超出其范圍,結果是取模後的值。如unsigned char c = -1; //假設char占8bit,c的值為255
  • 有符號類型賦值超出其范圍,結果未定義。如signed char c2 = 256; //假設char占8bit,c2的值未定義
  • 切勿混用帶符號類型和無符號類型。
  • 算數表達式中既有無符號數又有帶符號數,帶符號的數會轉換成無符號的數 

5.初始化與賦值

初始化和賦值是兩個完全不同的操作。

  • 定義於任何函數體外的之外的變量被初始化成0
  • 定義於函數體(塊作用域)內的內置類型的對象如果沒有初始化,則其值未定義。養成初始化內置變量的習慣。
  • 類的對象如果沒有顯式地初始化,則其值由類確定。

6. 聲明與定義

  • 聲明使得一個名字為程序所知,定義會申請存儲空間,還可能為其賦初始值
  • (分離式編譯) 如果想聲明一個變量而非定義它,就在變量名前添加關鍵字extern,而且不要顯式地初始化變量
  • 對於復雜的聲明語句,可以從變量名從右往左理解
  • 變量能且隻能被定義一次,但是可以被多次聲明 

7. C++關鍵字

8. const

8.1 初始化

默認狀態下,const對象僅在文件內有效。

在編譯的過程中,編譯器會把所有用到該const變量的地方都替換成相應的值。所以編譯器必須知道變量的初始值,如果存在多個文件的情況下,每個文件必須知道const的初始值(const對象也必須初始化)。但由於默認狀態下,const對象僅在文件內有效,當多個文件同時出現同名的const變量時,其實就相當於分別定義瞭不同的變量。

如果想隻定義一次怎麼做呢?

隻需要無論是聲明還是定義都標記extern關鍵字即可。

//file1.cpp
extern const i=1;
//file2.cpp
extern const i;

如果想要在多個文件之間共享const 對象,必須在變量之前添加extern關鍵字

8.2 const引用

定義:把引用綁定到const對象上,即為對常量的引用(reference to const)。
引用類型必須與其所引用對象類型一致(但是有兩個例外),

8.2.1 與普通引用不同的是不能讓非常量引用指向一個常量變量。

    int i= 0;
    const int &ci = i; 
    int &ri = ci;

在這裡插入圖片描述

因為非常量引用可以修改它所綁定的對象,但是常量引用不能,如果非常量引用指向一個常量變量合理的話,那麼非常量引用可以修改它所綁定的對象的值,這顯然是不正確的。

8.2.2 初始化常量引用時允許將以任意表達式作為初始值,隻要表達式的結果能轉換成對應類型即可。

8.2.2.1允許常量引用綁定分常量的對象、字面值,甚至一個一般表達式

    double dval = 3.14;
    const int &ri = dval;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

編譯器會將上面代碼變成:

    const int temp= dval;
    const int &ri = temp;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

在這種情況下面,ri綁定瞭一個臨時量

ri不是常量引用的時候會發生錯誤:

    double dval = 3.14;
    int &ri = dval; // error: invalid initialization of non-const reference of type ‘int&' from an rvalue of type ‘int'
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

編譯器會將上面代碼變成:

    int temp= dval;
    int &ri = temp;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

由於臨時值的特殊性,程序員並不能操作臨時值,而且臨時值隨時可能被釋放掉,所以,一般說來,修改一個臨時值是毫無意義的,據此,c++編譯器加入瞭臨時值不能作為非const引用的這個語義限制。

8.2.2.2 const引用可能用一個非const對象

    int i =0;
    int &ri = i;
    const int &ci = i;
    ci = 2; // error: assignment of read-only reference ‘ci'

常量引用僅對引用可參與的操作做出限定,對於引用的對象本身是不是一個長量未作限定

8.3 const與指針

指針的類型必須與其所指對象的類型一致(但是有兩個例外)

允許一個指向常量的指針指向一個非常量對象.

和const引用差不多,指針常量的指針也沒有規定所指的對象必須是一個常量

8.3.1 指向常量的指針和常量指針

指向常量的指針(指針可以改變,指針的值不可變):

const int x=1;
const int *p = &x;//p為指向常量x的指針
int *p = &x; //錯誤,因為p隻是個普通指針

常量指針(不變的指針而不是指針指向的值):

int x=0;
int *const p = &x;//p為常量指針(允許一個指向常量的指針指向一個非常量對象.)
const int xx=0;
const int *const pp= &xx;//pp為指向常量的常量指針(
    int x=0;
    int y = 11;
    const int *p = &x;//p為常量指針(允許一個指向常量的指針指向一個非常量對象.)
    int  *const cp = &x;
    const int *const pp= &x;//pp為指向常量的常量指針

    x = 1;
    std::cout<<"x:"<<x<<std::endl;
    std::cout<<"*p:"<<*p<<std::endl;
    std::cout<<"*cp:"<<*cp<<std::endl;
    std::cout<<"*PP:"<<*pp<<std::endl;
    p = &y;
    *p = 11; // error: assignment of read-only location ‘* p'
    *cp = 12;
     cp = &y;// error: assignment of read-only variable ‘cp'
    std::cout<<"*p:"<<*p<<std::endl;
    std::cout<<"*cp:"<<*cp<<std::endl;

小結:

常量指針,指針本身是一個常量,不可以改變指向的對象,但是可以改變指向對象的值,可以使用解引符改變地址所指向的值
指向常量的指針,可以重新指定指向新的對象,但是不可以改變對象的值.

    int errNumb =0;
    int *const curErr = &errNumb; // curerr 將一直指向errNumb errNumb 的值變化瞭curerr的值也跟著變化
    const double pi = 3.14159;
    const double *const pip = &pi; // pip 是一個指向常量對象的常量指針
    std::cout<<"curErr:"<<*curErr<<std::endl;
    std::cout<<"pip:"<<*pip<<std::endl;
    errNumb =1;
    std::cout<<"curErr:"<<*curErr<<std::endl;

輸出:

curErr:0

pip:3.14159

8.3.2 頂層const和底層const

指針如果添加const修飾符時有兩種情況:

頂層const:表示指針本身是一個常量
底層const:表示指針所指向的對象是一個常量

9. constexpr 和常量表達式

常量表達式(const experssion):是指
1.值不會改變 並且
2.在編譯過程就能得到計算結果的表達式。

字面量屬於常量表達式,用常量表達式初始化的const對象也是常量表達式。

9. 1 constexpr 變量:

一般來說,如果 你認定變量是一個常量表達式,那就把它聲明成constexpr類型函數:

constexpr函數是指能用於常量表達式的函數。

應遵循的約定:函數返回類型及所有形參的類型都是字面值類型,而且函數體中必須有且隻有一條return 語句。

constexpr函數體內可以有其他語句,隻要這些語句在運行時不執行任何操作。(例如,空語句、類型別名和using聲明等)

constexpr函數被隱式地指定為內聯函數。

9. 2字面值類型

常量表達式的值需要在編譯時就得到計算,因此對聲明constexpr時用到的類型必須有所限制。因為這些類型一般比較簡單,值也顯而易見、容易得到,就把它們稱為“字面值類型”(literal type)。

字面值類型范圍:

算術類型、引用和指針都屬於字面值類型。某些類也是字面值類型,它們可能含有constexpr函數成員。自定義類Sales_item、IO庫、string類型不屬於字面值類型。

9. 3constexpr 和指針

盡管指針和引用可以定義成constexpr,但它們的初始值受到嚴格限制。一個constexpr指針的初始值必須是nullptr、0或存儲於某個固定地址中的對象。

constexpr int *q = nullprt 等價於 int const *q = nullprt

函數體內定義的變量一般來說並非存放在固定地址中,因此constexpr指針不能指向這樣的變量。定義於函數體外的對象其地址固定不變,能用來初始化constexpr指針。

允許函數定義一類有效范圍超出函數本身的變量,如局部靜態變量,這類變量和定義在函數體之外的變量一樣有固定地址,因此constexpr引用能綁定到這樣的變量上,constexpr指針也能指向這樣的變量。

10. 處理類型

10.1 類型別名 typeof

typedef double a;
typedef wages base,*p; //等價於 double base ,*p;

using

using SI = Sales_item;
SI item;//等價於 Sales_item item;

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: