C++Primer-第二章 變量和基本類型

第二章 變量和基本類型

數據類型是程序的基礎:它告訴我們數據的意義及我們能在數據上執行的操作

2.1 基本數據類型

C++定義了一套包括算術類型空類型在內的基本數據類型

2.1.1 算術類型

算術類型:

  • 整型
  • 浮點型
    在這裏插入圖片描述
    布爾類型的取值是或者
    帶符號類型和無符號類型
    除去布爾型和擴展的字符之外,其他整型可以劃分爲 帶符號的無符號的。帶符號的可以表示正數、負數、0,無符號類型僅可以表示大於等於0的值。
    • 類型 int、short、long、和 long long都是帶符號的
    • 類型 unsigned int、unsigned short、unsigned long和 unsigned long long都是無符號的

如何選擇類型

  • 當明確知曉數值不可能爲負時,選用無符號類型
  • 使用int執行整數運算。如果數值超過int的數值範圍,則選用long long
  • 在算術表達式中不要使用char或bool,只有在存放字符或者布爾值時才使用他們
  • 執行浮點數運算選用double

2.1.2 類型轉換

對象的類型定義了對象能包含的數據和能參與的運算,其中一種運算被大多數類型支持,就是將對象從一種類型轉換爲另一種類型。
類型所能表示的值的範圍決定了轉換的過程:

  • 當我們把一個非布爾類型的算術值付給布爾類型時,初始值是0 則結果爲false,否則結果爲true
#include <iostream>
using namespace std;
int main()
{
	int intVal=12;
	bool boolVal=intVal;
	cout<<"boolVal:"<<boolVal<<endl;
	return 0;
}
結果: 
boolVal:1
  • 當我們把一個布爾值賦給非布爾類型時,初始值爲false 則結果爲0,初始值爲true 則結果爲1
#include <iostream>
using namespace std;
int main()
{
	bool boolTrueVal=true;
	bool boolFalseVal=false;
	int intTrueVal=boolTrueVal;
	int intFalseVal=boolFalseVal;
	cout<<"intTrueVal:"<<intTrueVal<<endl;
	cout<<"intFalseVal:"<<intFalseVal<<endl;
	return 0;
}
結果:
intTrueVal:1
	 intFalseVal:0
  • 當我們把一個浮點數賦給整數類型時,進行了近似處理。結果值將僅保留浮點數中小數點之前的部分。
#include <iostream>
using namespace std;
int main()
{
	double doubleVal=3.1415926;
	int intVal=doubleVal;
	cout<<"intVal:"<<intVal<<endl;
	return 0;
}
結果:
intVal:3
  • 當我們把一個整數值賦值給浮點類型時,小數部分記爲0。如果該整數所佔的空間超過了浮點類型的容量,經度可能有損失。
#include <iostream>
using namespace std;
int main()
{
	int intVal=120;
	float doubleVal=intVal;
	cout<<"doubleVal:"<<doubleVal<<endl;
	return 0;
}
結果:
doubleVal:120
這地方在輸出結果的時候給格式化了
  • 當我們賦給無符號類型一個超出它表示範圍的值時,結果是初始值對無符號類型表示數值總數取模後的餘數。
#include <iostream>
using namespacestd;
int main()
{
	//unsigned int的最大值爲 4294967296
	unsigned int intVal;
	intVal = 9999999999999999999;
	cout << "intVal:" << intVal << endl;
	//取模
	unsigned intMod = 9999999999999999999 % 4294967296;
	cout << "intMod:" << intMod << endl;
	return 0;
}
結果:
intVal:2313682943
intMod:2313682943
  • 當我們賦給帶符號類型一個超出它表示範圍的值時,結果是錯誤值。(此處應注意)
#include <iostream>
using namespace std;
int main()
{
	int intVal=9999999999999999999;
	cout<<"intVal:"<<intVal<<endl;
	return 0;
}
結果:
intVal:-1981284353

2.1.3 字面值常量

一個形如42的值被稱作字面值常量。

  • 整型和浮點型字面值
20 //十進制
024 //八進制
0x14 //十六進制
  • 字符和字符串字面值
'a' //字符字面值
"Hello world!" //字符串字面值
  • 轉義序列
名稱 字符 名稱 字符 名稱 字符
換行符 \n 橫向製表符 \t 報警(響鈴)符 \a
縱向製表符 \v 退格符 \b 雙引號 \"
反斜槓 \ 問號 \? 單引號 \’
回車符 \r 進紙符 \f
  • 指定字面值類型
L'a' //字符串類型字面值,類型是wchar_t
u8"hi" // utf-8字符串字面值
42ULL //無符號整型字面值,類型是unsigned long long

在這裏插入圖片描述

  • 布爾字面值和指針字面值
//布爾字面值是 true 和 false
bool trueVal=true;
bool falseVal=false;
//nullptr是指針字面值(指針模塊)
int* pIntVal=nullptr;

2.2 變量

變量提供一個具名的,可供程序操作的存儲空間。
個人理解就是:有很多存放不同物品的倉庫,我把每個倉庫都取上名字。

2.2.1 變量的定義

變量定義的基本形式:類型說明符 由一個或多個變量名

int sum=0,value=0,units_sold=1; //sum value和units_sold都是int
Sales_item item; //item類型是Sales_item
  • 初始值
//price先被定義並賦值,隨後被用於初始化discount
double price=109.99,discount=price*0.16;
//age 先定義  然後賦值
int age;
age=10;
  • 列表初始化
int units_sold=0;
int units_sold={0};
  • 默認初始值
    如果定義變量時沒有指定初值,則變量被默認初始化,此時變量被賦予了默認值
    全局變量在聲明時會把初始化;局部變量在聲明時不會被初始化

2.2.2 變量聲明和定義的關係

  • 聲明:使得名字爲程序所知。(例如:做飯的時候需要有湯勺,鍋,刀等等。只是有這些東西,用不用另說)
  • 定義:負責創建於名字關聯的實體。(例如:做飯的時候使用到的湯勺,鍋等等)

2.2.3 標識符

C++的標誌符由 字母、數字、下劃線組成,其中必須以字母或下劃線開頭。

  • 變量命名規範
    • 標誌符要能體現實際含義。
    • 變量名一般用小寫字母,如index,不要用Index或者INDEX。
    • 用戶自定義類型一般以大寫字母開頭,如Sales_item。
    • 如果標識符由多個單詞組成,則單詞間應有明顯區分,如student_loan或studentLoan。

對於命名規範來說,若能堅持,必將有效

命名規範不是c++語言硬性規定的,只是在寫代碼的時候便於識別和理解的約定,團隊可以根據情況自行規定風格。可以參考 Google、微軟等大公司的風格。

  • c++關鍵字
    在這裏插入圖片描述
  • C++操作符替換名
    在這裏插入圖片描述

2.2.4 名字的作用域

作用域是程序的一部分,在其中名字有其他特定的含義。C++語言中大多數作用域以花括號分隔。(說的不明不白的)
個人理解:作用域就是名字的使用範圍。例如:學校裏的班級,在這個學校裏可以直接說哪個班,但是在學校以外就要指定學校名稱+班級名稱。

  • 嵌套作用域
    作用域能彼此包含,被包含的作用域成爲內層作用域,包含着別的作用域的作用域稱外層作用域

2.3 符合類型

符合類型是指基於其他類型定義的類型。

2.3.1 引用

引用 爲對象起了另一個名字,引用類型引用另外一種類型。通過將聲明符寫成&d的形式定義引用類型,其中d是聲明的變量名。

int iVal=1024;
int &refVal=iVal; //refVal指向iVal
int &refVal2; //報錯:引用必須被初始化
  • 引用即別名

引用並非對象,相反的,它只是爲一個已經存在的對象所取的另外一個名字。
對引用類型的所有操作都是在與之綁定的對象上進行的。

2.3.2 指針

  • 指針是執行另外一種類型的符合類型。與引用類似,指針也實現了對其他對象的間接訪問。
    • 指針本身就是一個對象,允許對指針賦值和拷貝。與引用不同。
    • 指針無需在定義時賦值。
  • 獲取指針的地址
    &:取地址符
int iVal=20;
int *p=&iVal; //p 存放變量iVal的地址,或者說p指向變量iVal的指針
指向
*p指針
iVal
  • 指針值
    指針值應屬於下列4中狀態之一:
  1. 指向一個對象。
  2. 指向緊鄰對象所佔空間的下一個位置。
  3. 空指針,意味着指針沒有指向任何對象。
  4. 無效指針,依舊是上述情況之外的其他值。
  • 利用指針訪問對象
    *:解引用符(操作符*)
int iVal=40;
int *p=&iVal;//p存放着變量iVal的地址
cout<<"*p:"<<*p<<endl;//由符號*得到指針p所指向的對象
  • 空指針
    空指針:不指向任何對象的指針。
int *p1=nullptr;//等價與 int *p1=0;
int *p2=0;
//需要引入 cstdlib頭文件
int *p3=NULL;
//上方三者等價
  • 賦值和指針
int i=42;
int *pi=0;	//pi被初始化,但沒有指向任何對象
int *pi2=&i;//pi2被初始化,存有i的地址
int *pi3;	// pi3被定義,值無法確定

pi3=pi2;	//pi3和pi2指向同一個對象i
pi2=0;		//pi2不指向任何對象
  • void* 指針
    void*是一種特殊的指針,可用於存放任意對象的地址。
double obj=3.14,*pd=&obj;
void *pv=&obj;//正確:void* 能存放任意類型對象的地址
pv=pd; //pv可以存放任意類型的指針

2.3.3 理解符合類型的聲明

  • 定義多個變量
int* p1,p2;		//p1是指向int的指針,p2是int  容易產生誤導
int *p1,p2;		//推薦寫法
  • 指向指針的指針
int iVal=1024;
int *pi=&iVal;		//pi指向一個int類型的數
int **ppi=&pi;		//pi指向一個int型的指針

在這裏插入圖片描述

  • 指向指針的引用
int i=1024;
int *p;		//p是一個int型指針
int *&r=p;	//r是一個對指針p的引用
	
r=&i;		//r引用了一個指針,因此給r賦值&i就是令p指向i
*r=0;		//解引用r得到i,也就是p指向的對象,將i的值改爲0

2.4 const限定符

const: 用const修飾的變量的值不能被改變。

  • 初始化和const
int i=1024;
const int ci=i;		//正確:i的值被拷貝給了ci
int j=ci;			//正確:ci的值被拷貝給了j

2.4.1 const的引用

  • 初始化和對const的引用
int i=1024;			//定義i
const int &r1=i;	//正確:允許將const int&綁定到一個普通int對象上
const int &r2=2048;	//正確:r1是一個常量引用
const int &r3=r1*2;	//正確:r3是一個常量引用
int &r4=r1*2;		//錯誤:r4是一個普通的非常量引用

2.4.2 指針和const

  • 指向常量的指針指針常量
const double pi=3.14;//pi是一個常量,它的值不能改變
double *ptr=&pi;	 //錯誤:ptr是一個普通指針
const *cptr=&pi;	 //正確:cptr可以指向一個雙精度常量
*cptr=1024;			 //錯誤:不能給*cptr賦值

也就是說 指針保存的地址是能改變的,保存的地址指向的對象是不可以變的

  • const指針常量指針
    指針是對象而引用不是,因此就像其他對象類型一樣,運行把指針本身定位常量。
    常量指針必須初始化,而且一旦初始化完成,則它的值就不能再改變了。
int errNumb=0;
int *const curErr=&errNumb; //curErr將一直指向errNumb
const double pi=3.14159;
const double *const pip=&pi; //pip是一個指向常量對象的常量指針

指針所保存的地址是不可以改變的,保存地址所指向的對象是可以改變的

2.4.3 頂層底層const

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

2.4.4 constexpr和常量表達式

  • 常量表達式:值不會改變並且編譯過程就能得到計算結果的表達式。
const int max_files=20;		//max_files是常量表達式
const int limit=max_files+1;	//limit是常量表達式
int staff_size=27;				//staff_size不是常量表達式
const int sz=get_size();		//sz不是常量表達式
  • constexpr變量
    C++11新標準規定,運行將變量聲明爲constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式。
constexpr int mf=20;		//20是常量表達式
constexpr int limit=mf+1;	//mf+1是常量表達式
constexpr int sz=size();	//只有當size是 一個constexpr函數時,纔是一個正確的聲明語句
  • 指針和constexpr
    在constexpr生命中如果定義了一個指針,限定符constexpr僅對指針有效,與指針所指的對象無關。
const int *p = nullptr;	//p是一個指向整型變量的指針
constexpr int *q = nullptr;	//q是一個指向整數的常量指針
// p和q相差甚遠,p是一個指向常量的指針,而q是一個指針常量,
// 其中關鍵字constexpr把它所定義的對象置爲頂層const

2.5 處理類型

2.5.1 類型別名

類型別名 是一個名字,它是某種類型的同義詞。

  • 定義類型別名
    • typedef:
    typedef double wages;	//wages是double的同義詞
    typedef wages base, *p; //base是double的同義詞,p是double*的同義詞
    
    • using:
    using SI=Sales_item;	//SI是Sales_item的同義詞
    
  • 指針、常量和類型別名
	typedef char *pstring;
	const pstring cstr=0;	//cstr是指向char的常量指針
	const pstring *ps;		//ps是一個指針,它的對象是指向char的常量指針

2.5.2 auto類型說明符

C++11新標準引入了auto類型說明符,用它就能讓編譯器替我們去分析表達式所屬的類型。

int i=0,&r=i;
auto a=r;	//a是一個整數(r是i的別名,而i是一個整數)

auto一般會忽略掉頂層const,同是底層const會保留下來

int i=0,&r=i;
const int ci=i,&cr=ci;
auto b=ci;	//b是一個整數(ci的頂層const特性被忽略掉了)
auto c=cr;	//c是一個整數(cr是ci的別名,ci本身是一個頂層const)

2.5.3 decltype 類型指示符

C++11新標準引入了第二種類型說明符declytype,它的作用是選擇並返回操作數的數據類型

decltype(f()) sum=x; //sum類型就是函數f的返回類型
const int ci=0,&cj=ci;
decltype(ci) x=0;	//x 類型爲  const int
decltype(cj) y=x;	//y的類型是 const int&, y綁定到變量x
decltype(cj) z;		//錯誤:z的類型是一個引用,必須初始化
  • decltype和引用
int i=1024,*p=&i,&r=i;
decltype(r+0) b;	//正確:加法的結果是int,因此b是一個(未初始化的)int
decltype(*p) c;		//錯誤:c是int&,必須初始化

2.6 自定義數據結構

2.6.1 定義Sales_item

struct Sales_item
{
	string bookNo;
	unsigned int units_sold=0;
	double revenue=0.0;
}salesItem,*salesItem;

2.6.2 使用Sales_item類

#include <iostream>
#include <string>
using namespace std;

struct Sales_item
{
	string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
}saleItem,*p_SalesItem; //此處聲明一個實例,和一個指針引用

int main()
{
	saleItem.bookNo = "C++從入門到精通";
	saleItem.units_sold = 100;
	saleItem.revenue = 89;
	cout << "bookNo:" << saleItem.bookNo<<"\t unit_sold:"<<saleItem.units_sold<<"\t revenue:"<<saleItem.revenue << endl;
	p_SalesItem = new Sales_item();
	p_SalesItem->bookNo = "Effective C++";
	p_SalesItem->units_sold = 200;
	p_SalesItem->revenue = 99;
	cout << "bookNo:" << p_SalesItem->bookNo << "\t unit_sold:" << p_SalesItem->units_sold << "\t revenue:" << p_SalesItem->revenue << endl;
	delete(p_SalesItem);
	return 0;
}

2.6.3 編寫自己的頭文件

#define: 把一個名字設定爲預處理變量
#ifdef : 當且僅當變量定義時爲真(以 #endif爲止)
#ifndef: 當且僅當變量未定義時爲真(以 #endif爲止)

#ifndef SALES_ITEM_H
#define SALES_ITEM_H
#include <string>
using namespace std;
struct Sales_item
{
	string bookNo;
	unsigned units_sold=0;
	double revenue=0.0;
}saleItem,*p_saleItem;
#endif // SALES_ITEM_H

下面的頭文件和上面的等價

#pragma once   //此文件只引用一次
#include <string>
using namespace std;
struct Sales_item
{
	string bookNo;
	unsigned units_sold=0;
	double revenue=0.0;
}saleItem,*p_saleItem;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章