類型轉換
隱式轉化
不需要人爲參與而產生的默認轉稱爲隱式轉化。
隱式轉化,是計算機語言實現層面最難的,指針是使用層面最難的。
當下,我們只是瞭解的一些規則,不考慮溢出,截斷和有無符號。
後面博客會有更爲詳細的說明。
隱式轉換規則:
算數轉換
整型提升
char short int 等類型在參與運算時,首先提升到 int,這種現象叫作整型提升。
整型提升的原理是符號擴充。
例如: 不同的整型類型數據相加。
char + char
char + char
char + int
char + short
short + int
我們用代碼加以解釋:
#include <stdio.h>
itn main()
{
char ch = 1;
short sh = 1;
int in = 1;
printf("sizeof(ch) = %d\n", sizeof(ch));
//不涉及計算 ch是一個字節
printf("sizeof(ch + ch) = %d\n", sizeof(ch + ch));
//涉及計算 ch爲4個字節
//所以體現出來 char類型在參與計算的時候提升到int類型
printf("sizeof(sh) = %d\n", sizeof(sh));
//不涉及計算 ch是一個字節
printf("sizeof(sh + ch) = %d\n", sizeof(sh + ch));
//涉及計算 ch爲4個字節
printf("sizeof(in) = %d\n", sizeof(in));
//不涉及計算 ch是一個字節
printf("sizeof(in + ch) = %d\n", sizeof(in + ch));
//涉及計算 ch爲4個字節
return 0;
}
輸出結果爲:
給出兩種不同的整型的變量,得到計算之後的結果所佔字節的大小,就可以得到不同的整型在計算的過程中轉化之後的類型。
上面的方法也就是用sizeof(兩個不同的整型進行計算)來得到整型提升之後的類型
(上面這種方法用於驗證及其重要)
整型提升原理:符號擴充
#include <stdio.h>
int main()
{
char ch ;
int in;
ch = -1;
in = ch;
printf("ch = %d\n", ch);
printf("in = %d\n", in);
//涉及符號擴充
return 0;
}
運行結果爲:
符號擴充
小空間到大空間由於符號擴充的行爲,符號保持不變
小數據賦值給大變量 符號不丟失
大數據賦值給小變量 有可能會溢出導致數據丟失
#include <stdio.h>
int main()
{
char ch;
int in;
ch = 127;
in = ch;
printf("ch = %d\n", ch);
printf("in = %d\n", in);
return 0;
}
輸出結果爲:
以上結果我們給出圖解:
我們在這裏主要注意有符號和無符號的整型數據在類型轉換時候的區別,上圖紅色部分表示數字-1從char類型進入到int類型的過程,黑色部分表示數字127從char類型進入到int類型的過程,我們在char類型裏面用物理存儲方式保存之後,只需要在int類型裏面保證符號爲不改變。
以上給出的分析圖需要讀者深刻理解,在這裏再強調,存儲數據的時候是數據的補碼存儲,符號位是通過補碼的第一位來判斷,例如char類型的127物理存儲第一位是0 那麼進入int類型存儲的過程中首位符號位也必須是0來保證char類型進入到int類型之後的符號爲不變。-1同理也要保證符號位不變,從而來保證數據的準確性。
整型提升,小範圍向大範圍空間轉化,由於符號擴充的存在,保證數據的正確性。
大變量賦值給小數據有可能就會發生數據丟失。取決於賦值之後是否溢出。
混合提升
前面我們介紹的都是整型的類型提升,那麼如果是int float double longlong 等類型在一起運算呢?
我們首先給出不同類型所佔的大小,然後用一種巧妙的方式得出以上類型在計算的時候類型的提升轉換。
不同的大小字節是否一定是小字節轉化爲大字節呢?
我們接下來進行說明
int類型和float類型計算時,int 向 float類型轉化
**int類型和double類型計算時,int 向 double轉化 **
float類型和double類型計算時,float類型向double類型轉化
我們經過驗證
float 類型和 long long類型進行計算的時候 long long 類型會轉換爲float類型
long long類型和 double類型進行計算的時候 long long 類型會轉換爲double類型
以下給出代碼並且進行解釋:
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(float));
float f = 3.4;
long long i = 5;
//類型不匹配的時候輸出結果是錯誤的 ********* 方法
printf("f + i = %d\n", f + i);
printf("f + i = %f\n", f + i);
return 0;
}
輸出結果爲:
由於float +long long 的3.4 和 5 最終結果以%f格式打印出來正確結果,所以float類型和long long類型進行計算的時候轉換爲float類型。
我們每一個混合提升不同類型的計算都使用這種方法,只是修改類型,用參與運算的兩種類型分別輸出,如果類型匹配輸出正確,那麼就是轉化之後提升的類型,輸出不正確,所輸出的格式類型就是兩種格式在計算的時候轉換提升的類型。
所有的混合提升都可以使用這種方法進行檢驗。我們在前面已經給出了結論,其他的類型讀者可以自行檢驗。
使用的要點就是:類型不匹配的時候運行結果是錯誤的。
賦值轉換
整型和實型之間是可以相互賦值的。賦值的原則是,一個是加零,一個是去小數位。
#include <stdio.h>
int main()
{
int in = 10; //整數向小數賦值 加小數點
float fl = 3.14;//小數向整數賦值去除小數 整數部分賦值
fl = in;
printf("%f", fl);
return 0;
}
輸出結果爲:
整數向小數賦值 加小數點
#include <stdio.h>
int main()
{
int in = 10; //整數向小數賦值 加小數點
float fl = 3.14;//小數向整數賦值去除小數 只有整數部分賦值
in = fl;
printf("%d", in);
return0;
}
輸出結果爲:
帶有小數點的數向整數賦值去除小數點及其之後的數 只有整數部分賦值
強制轉換
隱式類型轉化,是有缺陷的,當隱式類型轉化不能滿足我們的需求時,就需要強制類型轉化。
格式:
(類型) 待轉表達式
#include <stdio.h>
int main()
{
int a = 10;
int b = 3;
printf("%d\n", a / b);
printf("%f\n",(float)a / b);
printf("%f",10 / 3.0);
return 0;
}
輸出結果爲:
大多數情況下使用強制類型轉化的原因是程序設計的不夠好,是對於設計缺陷的一種補救
編譯器使用注意
最後我們說明一點小問題:本篇文章在使用scanf函數時使用了scanf_s 其實兩個是一樣的,只不過vs2019改進了scanf的一些安全問題,這種寫法來自與編譯器,讀者在編寫代碼的時候只需要使用scanf即可,效果完全相同,如果讀者也使用的是vs2019並且也想要使用scanf而不是用scanf_s需要宏定義:
#define _CRT_SRCURE_NO_WARNINGS
就可以解決。
小結
在前面我們用大量時間和內容說明一些底層知識是很有必要的,這對於我們之後的整個學習過程和理解都有很大的作用,對於我們理解數據類型和操作,在真正認識類型的基礎上去使用和操作,才能把類型用的更加準確和減少我們的一些錯誤的發生,所以我認爲是很有必要的。