網站首頁 編程語言 正文
1. 前言
編寫程序時,數據確定后,就需要為數據提供相應的處理邏輯(方案或算法)。所謂邏輯有2種存在形態:
- 抽象形態:存在于意識形態,強調思考過程,與具體的編程語言無關。
- 具體形態:通過代碼來實現。需要使用表達式描述完整的計算過程。
表達式由2個部分組成:
- 數據。也可稱為操作數。
- 運算符。
運算符是計算機語言提供的能對數據進行基本運算操作的功能體。開發者在實現自己的邏輯運算時,需要組合這些運算符來描述自己的邏輯運算過程。
Tip:?可以把C++
的運算符看成一種特殊語法格式的函數,或把C++
中的函數當成一種特殊的運算符。
在使用運算符時,需要遵守下面的2個基本原則:
- 運算符對操作的數據有內置的類型要求。如數學運算符要求操作數是數字類型。
- 如果運算符需要多個操作數時,則要求數據類型必須相同。如果出現類型不一致時,編譯器會試著把不同類型的數據轉換成同類型的數據后再進行運算。開發者也可以顯示進行強制類型轉換。
2. 運算符種類
C++
中的運算符非常多,如下是幾類常用的運算符:
- 算術運算符。
- 邏輯、關系運算符。
- 賦值運算符。
- 遞增、遞減運算符。
- 成員訪問運算符。
- 條件運算符。
- 位運算符。
-
sizeof
?運算符。 - 逗號運算符。
使用運算符前,需要理解如下幾個概念:
- 運算符的優先級:?不同類別中的運算符的優先級是不相同的。當在一個表達式中出現多個運算符時,則需要根據運算符的優先級進行先后運算。
- 運算符的操作數:?作用于一個操作數的運算符為一元運算符,作用于兩個操作數的運算符為二元運算符。
C++
中還有一個可作用于三個操作數的條件運算符。 - 結合性:?當復雜表達式中的多個運算符的優先級相同時,則要根據運算符的結合性進行運算。如?
100/4*8
這個表達式,/
和*
的優先級是相同,因乘、除都是具有從左到右的結合性。所以先計算100/4=25
再計算25*8
。
Tip:?只有當兩個運算符作用于同一個操作數時,優先級和結合性才有意義。
C++
中的基礎運算符較多,且因C++
是弱類型語言,每一種運算符在使用過程中都存在很多細節問題。算術運算符又是運算符中的基礎運算符。
本文試圖通過講解清楚算術運算符,讓閱讀者了解使用C++
運算符時應該注意的事項。
3. 算術運算符
3.1 功能描述
算術運算符用來對數字型數據進行數學語義上的加、減、乘、除。此類中有?5個運算符:
-
+
:對2個數字類型的數據進行數學語義上的加法運算。 -
-
:對2個數字類型的數據進行數學語義上的減法運算。 -
*
:對2個數字類型的數據進行數學語義上的乘法運算。 -
/
:對2個數字類型的數據進行數學語義上的除法運算。 -
%
:取余或取模操作運算符。運算結果是兩個操作數相除后的余數部分,不能用于浮點數據類型。
算術運算符是二元運算符。使用時,需要提供2個操作數。
3.2 運算符重載問題
C++
可以重載運算符,所謂重載運算符,指同一個運算符可以根據使用時的上下文信息,表現出不同的運算能力。如-
運算符, 當作為二元運算符時,用來對操作數進行相減操作。
int num1=30; int num2=20; //此處的 - 運算符表現出減法運算能力 int res=num1-num2; cout<<res<<endl; //輸出結果: 10
當作為一元運算符時,則是取負
的意思。如下代碼:
int num=-10; int num01=-num; cout<<num01<<endl; //輸出結果為 10,負負為正
同理,+
運算符也存在重載。
運算符重載是C++
中的一個特色。
對于有符號數據類型而言,如果在字面常量前面沒有顯示提供正、負符號,則默認為?+(正)
符號。
3.3 兩數相除的問題
當/
運算符作用于2個整型數字時,會得到舍棄小數點后的整數部分數值,或稱為兩數相除的商,意味著會丟失精度。
如下代碼:
int num1=7; int num2=3; int res=num1/num2; cout<<res<<endl; //輸出結果:2,丟失精度
如果要保留兩個數字相除的精度,則應該以浮點數據類型的身份進行相除。
double num1=7; double num2=3; double res=num1/num2; cout<<res<<endl; //輸出結果:2.33333
%
運算符作用于2個整型類型的數據時,運算結果是2個數字相除之后的余數部分。如下代碼:
int num1=5; int num2=3; int res=num1 % num2; cout<<res<<endl; //輸出結果:2 。
%
用于浮點數據類型相除時,會出現編譯錯誤。也就是?%
只能用于整型數據的運算,不能用于浮點數據類型。
3.4 關 于/和%運算符的正、負問題
當2個操作數據都是正數時
int num1=21; int num2=8; int res=num1 / num2; cout<<" / 運算:"<<res<<endl; res=num1 % num2; cout<<" % 運算:"<<res<<endl;
/
和%
動算符的輸出結果都是正數。
/ 運算:2
% 運算:5
當2個操作數都為負數時
int num1=-21; int num2=-8; int res=num1 / num2; cout<<" / 運算:"<<res<<endl; res=num1 % num2; cout<<" % 運算:"<<res<<endl;
輸出結果,一個是正數,一個是負數。
?/ 運算:2
?% 運算:-5
當2個操作數中被除數為負,除數為正時
int num1=-21; int num2=8; int res=num1 / num2; cout<<" / 運算:"<<res<<endl; res=num1 % num2; cout<<" % 運算:"<<res<<endl;
輸出結果都是負數。
/ 運算:-2
% 運算:-5
當2個操作數中被除數為正,除數為負時
int num1=21; int num2=-8; int res=num1 / num2; cout<<" / 運算:"<<res<<endl; res=num1 % num2; cout<<" % 運算:"<<res<<endl;
輸出結果為一負一正。
/ 運算:-2
% 運算:5
結論:
- 當2個數字使用?
%
運算符進行相除操作時,運算結果的正負號與?num1
操作數(被除數)的正負號保持一致。 -
/
運算符運算結果的正負號和數學上的語義一致。兩個操作數都為正或為負時則正正得正,負負得正。兩個操作數為一正一負時:則正負得負。
3.5 數據溢出問題
在使用算術運算符時,有可能出現數據溢出現象。如下代碼:
short num=32767; short num01=num+1; cout<<num01<<endl;
輸出結果:
數字:-32768
無符號short(16位)
的類型數據的最大值是?32767
,在此數字上加一,num01
的值理論是上?32768
。但實際結果是?-32768
。因為?32768
已經超過short
范圍,編譯器會重新計算出一個新的結果(并不是預期值)。這種現象叫數據溢出。
對于無符號?short
,可以認為其有?2
?部分,一部分為負數,一部分為正數。當正數溢出后,會進入負數部分。
如下代碼,因溢出,超過了負數區域最小值,會溢出到正數區域。
short num1=-32768; short num2=num1-1; cout<<num2; //輸出結果:32767
數據溢出發生在當把數據類型范圍大的數據存儲到數據類型小的類型變量中時。
-
double
?數據存儲到?int
?類型變量中。 -
int
?類型的數據存儲到?short
類型變量中。 -
long long int
類型的數據存儲到?int
?類型變量中時。 - ……
數學運算符也可以用于指針類型運算,因指針變量其數據本質就是數字數據。但指針變量不能用于乘法和除法,加、減的語義是指針的向前后后移動,乘法、除法沒有語義價值。
3.6 類型轉換
根據運算符的基本使用原則,要求所有操作數的類型必須相同。
有時,在一個表達式中,即使存在多個操作數的類型不一致,也能正常工作。那是因為,編譯器會把不同的數據類型轉換成一致,然后再進行運算。
由編譯器完成的類型轉換,稱為自動(隱式)類型轉換:
-
整型提升:
C++
將bool
、char
、unsigned char
、signed char
和short
值轉換為?int
。這些轉換被稱為整型提升。 -
浮點提升:整型類型自動向浮點類型轉換,如?
int
向?double
轉換。這種轉換是不會存在數據丟失問題,但會產生空間浪費。 -
向下縮窄: 當目標類型小于原類型時,如
double
?向?int
轉換,int
類型向short
轉換時,這種轉換是可以的,但會發生數據丟失的情況。可能會得不到預期結果。
碗里的水倒到缸里,不會丟失水。
缸里面的水倒到碗里,如果缸里面的水很少,不夠或者剛夠一碗水,不會發生水丟失。但是,這里會有潛在丟失問題,因為生活常識告訴我們,缸里面的水往往是要超過一個碗所能盛下的容量。
所以,向下縮窄存在潛在的數據丟失風險。
如下代碼,其中發生了2次自動類型轉換,有數據丟失的潛在風險。
double num1=7; int num2=3; int res=num1/num2; cout<<res<<endl; //輸出結果: 2
-
浮點提升:
num2
中的數據會被轉換成double
數據類型,讓右邊的表達式符合同類型原則。此時,右邊表達式運算后的結果類型為?double
。這一步不會發生數據丟失問題。 -
向下縮窄:?左邊的
res
變量類型為int
?,編譯器會把右邊的double
類型結果轉換成?int
。如果數值大于int
類型范圍時,則會出現丟失精度問題。
如下代碼,則不會發生數據丟失問題:
double num1=7; int num2=3; double res=num1/num2; cout<<res<<endl; //輸出結果:2.33333
如下的代碼,也會發生自動類型轉換。
int num1=20; char num2='A'; int res=num1+num2; cout<<res<<endl; //輸出結果: 85
-
char
類型會轉換成?int
類型。 - 字符保存在計算機上時,需要對其進行數字編碼,字符轉換成?
int
的數字是底層的編碼數字。
如下代碼,也會發生自動類型。
int num1=20; bool num2=true; int res=num1+num2; cout<<res<<endl;
-
C++
中,bool
數據類型本質上就是int
類型。 -
true
會轉換為?1
。false
會轉換為0
。
3.7?{}賦值語法
C++
在進行自動類型轉換時,如果目標類型小于原類型時,也是能夠轉換的,這種現象叫縮窄。縮窄會存在潛存數據安全問題。C++11
提供了{}
賦值語法,會對超過范圍的縮窄進行編譯提示。如下代碼。
因?44555
?數字已經超過?char
?范圍,向下縮窄不被允許。
char c1= {44555};
因?X
是一個變量,在運行時,x
有可能被修改,并讓其值大于?char
數字范圍,向下縮窄不被允許。
int x=66; char c4={x};
3.8 強制類型轉換
C++
允許開發者顯式地進行類型轉換。語法格式有2種:
- (目標類型名)變量。
- 目標類型名(變量)。
強制類型轉換不會修改變量本身,而是創建一個新的值。用于表達式中進行計算。
double num1=23.6; //C++強制類型轉換語法 int num2=double(num1); cout<<num2<<endl; //C 強制類型轉換語法 num2=(double)num1; cout<<num2<<endl;
C++
還提供了 4 個類型轉換運算符,使得轉換過程更規范。這里只做簡要介紹,有興趣者可以深入了解一下。
-
dynamic_cast
。在類層次結構中進行向上轉換。 -
const_cast
。用于執行只有一種用途的類型轉換,即改變值為?const
?或volatile
。 -
static_cast
。只有當類型之間可以隱式轉換時才能轉換。 -
reinterpret_cast
。用于一些有很大潛在危險的類型轉換。
3.9 auto 語法
auto
關鍵字在C++
的作用是自動類型推導。在聲明變量時,可以使用?auto
關鍵字,不指定變量的類型說明。編譯器會根據變量中所存儲的數據的類型自動推導出數據類型。
// num 是浮點數據類型 auto num=5.3; //num1 是整型數據類型 auto num1=4;
如?Python
、JS
就是一種動態語言,表現在數據類型可以底層編譯器自動識別。
雖然C++
有?auto
語法,但C++
歸屬于弱類型語言,在數據類型識別上,一半依賴于開發者的語法約束,一半依賴編譯器的自動識別。
4. 總結
因C++
語言的開放性,數據類型的自我適應性非常靈活。在一個表達式,當出現類型不同的情況時,編譯器會試圖進行各種類型上的轉換,讓表達式符合類型相同的運算原則。
寬松的好處是速度快,但也會帶來潛在的風險,開發者應該盡可能在語法上對數據類型進行約束,不要過于依賴編譯器。養成良好的編碼習慣。
原文鏈接:https://www.cnblogs.com/guo-ke/p/16399725.html
相關推薦
- 2022-11-06 Android淺析viewBinding和DataBinding_Android
- 2022-08-30 Spark中緩存和檢查點的區別
- 2023-05-18 Go語言中map集合的具體使用_Golang
- 2022-07-24 示例剖析golang中的CSP并發模型_Golang
- 2022-07-15 C#中字符串與字節數組的轉換方式_C#教程
- 2023-11-12 ubuntu18.04 開機后 USB 端口不能使用;臨時添加usb驅動導致鼠標鍵盤不可用ubunt
- 2022-08-15 SpringMVC異常處理流程總結
- 2022-01-19 標準時間格式轉換(正則寫法)
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支