學習系列之數據類型
一、數據類型初探
電腦是由什麽來存儲所使用的數據? 這個問題用一句話比較籠統的概括,那就是:電腦使用內存來記憶計算時所使用的數據。在現實生活中的數據各種各樣,整數、小數、字符串、字符等等,它們都類型是不一樣的,所以你要想在計算機中使用這些類型,就必須在內存中為它申請一塊合適的空間。
數據類型總結起來有以下幾點:
(1)C程序是一組函數和數據類型,C++程序是一組函數和類,而C#程序是一組類型聲明;
(2)類型是一種模板:模板本身不是數據結構,但它詳細說明了由該模板構造的對象的特征;
(3)C#提供了16種預定義類型:13種簡單類型(數值類型:int,float,double,decimal等;非數值類型:bool,char),3種非簡單類型(object,string,dynamic);
所有的預定義類型都直接映射到底層的.NET類型。C#的類型名稱其實就是.NET類型的別名,所以使用.NET的類型名稱也符合C#語法,不過並不鼓勵這樣做。在C#程序中,應當盡量使用C#類型名稱而不是.NET類型名稱;
(4)除了上面提到的16種預定義類型外,還可以創建自己的用戶定義類型,一共有6種用戶定義類型可以由用戶自己創建,它們是:類(Class)、結構體(Struct)、數組(Array)、枚舉(Enum)、委托(Delegate)和接口(Interface);
二 數據類型詳解
1、整型
2、浮點類型
float數據類型用於較小的浮點數,因為它要求的精度較低。
double數據類型比float數據類型大,提供的精度也大一倍(15位)。
如果在代碼中沒有對某個非整數值(如12.3)硬編碼,則編譯器一般假定該變量是double。
如果想指定該值為float,可以在其後加上字符F(或f),如:
float f = 12.3F;
3、decimal類型
decimal類型專門用於進行財務計算,使用decimal類型提供的28位的方式取決於用戶。
要把數字指定為decimal類型,可以在數字的後面加上字符M或(m),如:
decimal d=12.30M;
4、bool(布爾)類型
5、char字符類型
char類型的字變量是用單引號括起來的。 如‘A‘
如果把字符把在"A"(雙引號)內,編譯器會把它看作是字符串,從而產生錯誤。
6、引用類型(Object類型和字符串類型)
三 數據類型存儲雙雄:棧和堆
3.1 棧
(1)棧是一個內存數組,是一個LIFO(Last In First Out,後進先出)的數據結構。
(2)棧存儲幾種類型的數據:某些類型變量的值(主要是值類型);程序當前的執行環境;傳遞給方法的參數;
(3)棧具有幾種顯著的特征:數據只能從棧頂插入和刪除;將數據放到棧頂叫做入棧;將數據從棧頂移除叫做出棧;
3.2 堆
(1)堆是一塊內存區域,在堆裏可以分配大塊的內存用於存儲某類型(主要是引用類型)的數據對象;與棧不同,堆裏的內存能夠以任意的順序插入或移除;
(2)堆中的數據不能顯示地刪除,CLR中的自動GC(Garbage Collector,垃圾收集器)會自動清除無主(判斷程序代碼是否將不再訪問某數據項的時候)的堆內存對象。因此,我們可以驕傲地說:媽媽再也不用擔心我的垃圾了。
四、值類型和引用類型:屌絲和高富帥
(1)值類型:只需要一段單獨的內存,用於存儲實際的數據;TIP:對於值類型,數據存放在棧裏;(byte,int,long,float,double,struct,enum等)
(2)引用類型:需要兩段內存,第一段存儲實際的數據,它總是位於堆中;第二段是一個引用,指向數據在堆中的存放位置;TIP:對於引用類型,實際數據存放在堆裏,而引用存放在棧裏。(object,string,dynamic,class,interface,delegate,array)
(3)引用類型對象的數據始終存放在堆裏,無論它們是值類型還是引用類型。
五 、 數據類型轉換
數據類型在一定的條件下是可以相互轉換的,如將int型數據轉換成double型數據。C#允許使用兩種轉換方式:隱式轉換和顯式轉換。
1、隱式轉換
隱式轉換:從類型A到類型B的轉換可以在所有情況下進行,執行轉換的規則非常簡單,可以讓編譯器執行轉換。
隱式轉換不需要做任何工作,也不需要另外編寫代碼。如將int型數據轉換成double型數據:
int a = 10;double b = a;//隱式轉換
隱式轉換規則是:任何類型A,只要其取值範圍完全包含在類型B的取值範圍內,就可以隱式轉換為類型B。基於這個轉換規則,C#的隱式轉換不會導致數據丟失。需要註意的是我們最常用的簡單類型bool和string沒有隱式轉換。
2、顯式轉換
顯式轉換:從類型A到類型B的轉換只能在某些情況下進行,轉換規則比較復雜,應進行某種類型的額外處理。顯式轉換又叫強制類型轉換,顯式轉換需要用戶明確的指定轉換類型。如將double類型數據轉換成int類型數據:
double c = 10.5; int d = (int)c;//顯示轉換
提醒:
(1)、顯式轉換可能會導致錯誤。進行這種轉換時編譯器將對轉換進行溢出檢測。如果有溢出說明轉換失敗,就表明源類型不是一個合法的目標類型。無法進行類型轉換。
(2)、強制類型轉換會造成數據丟失,如上面的例子中,最終得到的d值為10。
3、通過方法進行類型轉換
(1)、使用ToString()方法。所有類型都繼承了Object基類,所以都有ToString()這個方法(轉化成字符串的方法)。
(2)、通過int.Parse()方法轉換,參數類型只支持string類型。註意:使用該方法轉換時string的值不能為為NULL,不然無法通過轉換;另外string類型參數也只能是各種整型,不能是浮點型,不然也無法通過轉換 (例如int.Parse("2.0")就無法通過轉換)。
int i; i = int.Parse("100");
(3)、通過int.TryParse()方法轉換,該轉換方法與int.Parse()轉換方法類似,不同點在於int.Parse()方法無法轉換成功的情況該方法能正常執行並返回0。也就是說int.TryParse()方法比int.Parse()方法多了一個異常處理,如果出現異常則返回false,並且將輸出參數返回0。
int i; string s = null; int.TryParse(s,out i);
(4)、通過Convert類進行轉換,Convert類中提供了很多轉換的方法。使用這些方法的前提是能將需要轉換的對象轉換成相應的類型,如果不能轉換則會報格式不對的錯誤。註意:使用Convert.ToInt32(double value)時,如果 value 為兩個整數中間的數字,則返回二者中的偶數;即 4.5 轉換為 4,而 5.5 轉換為 6。
(5)、實現自己的轉換,通過繼承接口IConventible或者TypeConventer類,從而實現自己的轉換。
六 值傳遞
(一)定義:
值類型在復制的時候,傳遞就是這個值得本身,例如:A同學的筆記復印了一份,給B同學,B同學任意修改筆記的內容,A同學都不會受到影響。
(二)例子:
int n1 = 10; int n2 = n1; n2 = 20; Console.WriteLine(n1); Console.WriteLine(n2); Console.ReadKey(); // 輸出結果:n1=10; n2=20;
分析:變量n1在棧區開辟一塊空間,其地址為0x000000e4a99fe094 ,並且為其賦初值10;
變量n2在棧區也開辟了一塊空間,其地址為0x000000e4a99fe090,並n1值賦給了 n2,n2:10
變量n2的值被重新賦值20,n2:20
(三)內存代碼:
&n1 0x000000e4a99fe094 n1: 0 &n1 0x000000e4a99fe094 n1: 10 &n2 0x000000e4a99fe090 n2: 0 &n2 0x000000e4a99fe090 n2: 10 &n2 0x000000e4a99fe090 n2: 20
由此可以看出:n1和n2在棧上的內存地址並不一樣,所以在改變任意其中的而一個值,另一個並不受其影響
(四) 內存示意圖:
七 、引用傳遞
(一)定義:
引用類型在復制的時候,傳遞的是對這個對象的引用
(二)例子:
Person p1 = new Person(); p1.Name = "張三"; Person p2 = p1; p2.Name="李四"; Console.WriteLine(p1.Name); Console.WriteLine(p2.Name);// 輸出結果:李四 李四
分析:
變量p1在棧中開辟一塊空間,地址為:0x00000095e5efdfd ,並且在堆中也有一塊空間,存放的是new Person();這個對象,其內存空間地址為:0x0000000000000000,並且為p1.name賦值張三
變量p2在棧中也開辟一塊空間,地址為:0x00000095e5efdfc8 ,此時,把p1對象賦值給p2對象,即p2也指向new Person()開辟的這塊空間,其內存空間地址為:0x0000000000000000,隨後,我們把p2.name賦值為 李四,那麽由於p1.name也指向這塊空間,所以,p1.name=”李四”
(四)內存代碼:
&p1 0x00000095e5efdfd p1: 0x0000000000000000 &p2 0x00000095e5efdfc8 p2: 0x0000000000000000
(五)內存示意圖:
八 特殊的引用類型
例子:
string s1 = "張三"; string s2 = s1; s2 = "李四"; Console.WriteLine(s1) Console.WriteLine(s2);
結果:s1=張三 s2=李四 string雖然為引用類型,但是string具有不可變性
九 、方法的參數類型
(一) 兩類參數的定義:
實參:具有實際意義的參數,一般在調用方法時,在方法的括號裏面傳遞的參數為實參
形參:在方法聲明時,方法名後面括號裏面的參數為形參,一般(值傳遞)形參是接收實參傳過來的值
(二) out參數:
1、定義:
在一個方法需要返回多個參數的時候,一般在參數列表裏面聲明out類型的參數,以便輸出多個返回值。其中,out參數只負責把結果輸出,不負責輸入參數。一般out聲明的變量需要在方法體內部賦值
2、例子:
class Program { static void Main(string[] args) { int[] ShuZi = { 1,2,3,4,5,6}; int max1; int min1; int sum1; JiSuan(ShuZi,out max1,out min1,out sum1); Console.WriteLine("數組的最大值為:{0},數組的最小值為:{1},數組的和為:{2}",max1,min1,sum1); Console.ReadKey(); } public static void JiSuan(int []number,out int max,out int min,out int sum) { max = number[0]; min = number[0]; sum = 0; for (int i = 0; i < 6; i++) { sum += number[i]; if (number[i] > max) { max = number[i]; } else { min=number[i]; } } } }
輸出結果為:最大值:6,最小值:1,和為21
(二)ref參數
1、定義:
即可以傳入值,又可以傳出值,雙向性
(二)例子;
class Program { static void Main(string[] args) { int n1=10; int n2=20; Console.WriteLine("兩個數字交換之前的值為n1={0},n2={1}",n1,n2); JiaoHuan(ref n1, ref n2); Console.WriteLine("兩個數字交換之後的值為n1={0},n2={1}", n1, n2); Console.ReadKey(); } public static void JiaoHuan( ref int n1,ref int n2) { int temp = n1; n1 = n2; n2 = temp; } }
結果為:
兩個數字交換之前的值為n1=10,n2=20
兩個數字交換之後的值為n1=20,n2=10
參考資料:《c#圖解教程》
學習系列之數據類型