信息的編碼表示
計算機(jī)要處理的信息是多種多樣的,如十進(jìn)制數(shù)、文字、符號、圖形、音頻、視頻等,這些信息在人們的眼里是不同的。但對于計算機(jī)來說,它們在內(nèi)存中都是一樣的,都是以二進(jìn)制的形式來表示,說白了就是1001 0011 0000 1011之類的。
前面我們已經(jīng)講到,計算機(jī)是以二進(jìn)制的形式來存儲數(shù)據(jù)的,它只認(rèn)識0和1兩個數(shù)字,我們在屏幕上看到的文字,在存儲到內(nèi)存之前也都被轉(zhuǎn)換成了二進(jìn)制(0和1序列)??上攵囟ǖ奈淖直厝粚?yīng)著固定的二進(jìn)制,否則將無法轉(zhuǎn)換。那么,怎樣將文字與二進(jìn)制對應(yīng)呢?這就需要有一套規(guī)范,計算機(jī)公司和軟件開發(fā)者都必須遵守。
ASCII碼
我們知道,一個二進(jìn)制位(Bit)有0、1兩種狀態(tài),一個字節(jié)(Byte)有8個二進(jìn)制位,有256種狀態(tài),每種狀態(tài)對應(yīng)一個符號,就是256個符號,從00000000到11111111。計算機(jī)誕生于美國,早期的計算機(jī)使用者大多使用英文,上世紀(jì)60年代,美國制定了一套英文字符與二進(jìn)制位的對應(yīng)關(guān)系,稱為ASCII碼,沿用至今。ASCII碼規(guī)定了128個英文字符與二進(jìn)制的對應(yīng)關(guān)系,占用一個字節(jié)(實際上只占用了一個字節(jié)的后面7位,最前面1位統(tǒng)一規(guī)定為0)。例如,字母 a 的的ASCII碼為 01100001,那么你暫時可以理解為字母 a 存儲到內(nèi)存之前會被轉(zhuǎn)換為 01100001,讀取時遇到 01100001 也會轉(zhuǎn)換為 a。
完整的ASCII碼表請查看:http://www.asciima.com/
Unicode編碼
隨著計算機(jī)的流行,使用計算機(jī)的人越來越多,不僅限于美國,整個世界都在使用,這個時候ASCII編碼的問題就凸現(xiàn)出來了。ASCII編碼只占用1個字節(jié),最多只能表示256個字符,我大中華區(qū)10萬漢字怎么表示,日語韓語拉丁語怎么表示?所以90年代又制定了一套新的規(guī)范,將全世界范圍內(nèi)的字符統(tǒng)一使用一種方式在計算機(jī)中表示,這就是Unicode編碼(Unique Code),也稱統(tǒng)一碼、萬國碼。
Unicode 是一個很大的集合,現(xiàn)在的規(guī)??梢匀菁{100多萬個符號,每個符號的對應(yīng)的二進(jìn)制都不一樣。Unicode 規(guī)定可以使用多個字節(jié)表示一個字符,例如 a 的編碼為 01100001,一個字節(jié)就夠了,”好“的編碼為 01011001 01111101,需要兩個字節(jié)。
為了兼容ASCII,Unicode 規(guī)定前0127個字符與ASCII是一樣的,不一樣的只是128255的這一段。
如果你希望將字符轉(zhuǎn)換為Unicode編碼,請查看:http://tool.chinaz.com/Tools/Unicode.aspx
完整的Unicode編碼請查看:unicode.org
對Unicode的支持,是檢驗現(xiàn)代編程語言的標(biāo)準(zhǔn)之一。
數(shù)據(jù)在內(nèi)存中的存儲
對于現(xiàn)代馮-諾依曼機(jī)型的計算機(jī)而言,內(nèi)存就是存儲器,是處理器(CPU)存取數(shù)據(jù)的地方?,F(xiàn)代計算機(jī)的內(nèi)存越來越大,基本上可以運行各種程序而不依賴外部存儲(硬盤等)。
數(shù)據(jù)存放在內(nèi)存中,就要在內(nèi)存中分出一個個存儲單元,寫入數(shù)據(jù)。這就帶來幾個問題:單元的規(guī)格(是一樣的還是不一樣的)、對這些單元如何管理(尋址、垃圾回收、安全問題等)、如何操作這些單元。在現(xiàn)代計算機(jī)科學(xué)中,這些內(nèi)容被歸納為數(shù)據(jù)結(jié)構(gòu),與算法并稱為編程的兩大基本知識。一些大師聲稱:數(shù)據(jù)結(jié)構(gòu)比算法更重要。
在數(shù)據(jù)存儲方面,需要考量的是存取的效率、安全性、空間占用和垃圾處理問題。
變量和常量
變量(Variable)是程序操作的基本單位。因為程序中要處理大量數(shù)據(jù),這些數(shù)據(jù)必須起個名字,不然怎么區(qū)分呢?我們寫:
var a; //這不但命名了一個變量,而且在內(nèi)存中劃分出一個空間用于存儲它
a = 10; //在這個空間寫入數(shù)值10,現(xiàn)在a的值就是10
變量名不僅僅是為數(shù)據(jù)起了一個好記的名字,還告訴我們數(shù)據(jù)存儲在哪里,使用數(shù)據(jù)時,只要提供變量名即可。在幾乎所有語言中,變量都需要先聲明再使用。當(dāng)我們聲明一個變量并賦值給它時,就是打開了一扇大門,把我們的意念注入到計算機(jī)之中。
程序中要處理各種各樣的數(shù)據(jù)?,F(xiàn)實生活中我們會找一個小箱子來存放物品,一來顯得不那么凌亂,二來方便以后找到。計算機(jī)也是這個道理,我們需要先在內(nèi)存中找一塊區(qū)域,規(guī)定用它來存放整數(shù),并起一個好記的名字,方便以后查找。這塊區(qū)域就是“小箱子”,我們可以把整數(shù)放進(jìn)去了。
C語言中這樣在內(nèi)存中找一塊區(qū)域:
int a;
int又是一個新單詞,它是 Integer 的簡寫,意思是整數(shù)。a 是我們給這塊區(qū)域起的名字;當(dāng)然也可以叫其他名字,例如 abc、mn123 等。
這個語句的意思是:在內(nèi)存中找一塊區(qū)域,命名為 a,用它來存放整數(shù)。
注意 int 和 a 之間是有空格的,它們是兩個詞。也注意最后的分號,int a表達(dá)了完整的意思,是一個語句,要用分號來結(jié)束。
不過int a;僅僅是在內(nèi)存中找了一塊可以保存整數(shù)的區(qū)域,那么如何將 123、100、999 這樣的數(shù)字放進(jìn)去呢?
C語言中這樣向內(nèi)存中放整數(shù):
a=123;
=是一個新符號,它在數(shù)學(xué)中叫“等于號”,例如 1+2=3,但在C語言中,這個過程叫做賦值(Assign)。賦值是指把數(shù)據(jù)放到內(nèi)存的過程。
把上面的兩個語句連起來:
int a;
a=123;
就把 123 放到了一塊叫做 a 的內(nèi)存區(qū)域。你也可以寫成一個語句:
int a=123;
a 中的整數(shù)不是一成不變的,只要我們需要,隨時可以更改。更改的方式就是再次賦值,例如:
int a=123;
a=1000;
a=9999;
第二次賦值,會把第一次的數(shù)據(jù)覆蓋(擦除)掉,也就是說,a 中最后的值是9999,123、1000 已經(jīng)不存在了,再也找不回來了。
因為 a 的值可以改變,所以我們給它起了一個形象的名字,叫做變量(Variable)。
int a;創(chuàng)造了一個變量 a,我們把這個過程叫做變量定義。a=123;把 123 交給了變量 a,我們把這個過程叫做給變量賦值;又因為是第一次賦值,也稱變量的初始化,或者賦初值。
你可以先定義變量,再初始化,例如:
int abc;
abc=999;
也可以在定義的同時進(jìn)行初始化,例如:
int abc=999;
這兩種方式是等價的。
變量的聲明和賦值
變量的聲明和賦值是所有程序語言的最基本命令。以下是各種語言的變量聲明和賦值語法:
C: 數(shù)據(jù)類型+變量名稱
int a;
a = 10;
int a, b, c; //一次聲明多個變量
int a = 10; //同時聲明和賦值
Java: 與C語言完全相同。
JavaScript: 統(tǒng)一使用var關(guān)鍵字來聲明變量,不區(qū)分?jǐn)?shù)據(jù)類型
var a;
a = 10;
var a = 20; //同時聲明和賦值
var name="Gates", age=56, job="CEO"; //一條語句,多個變量
ES6新增了let命令,用來聲明變量。它的用法類似于var
,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。
{ let a = 10; var b = 1;}a // ReferenceError: a is not defined.b // 1
上面代碼在代碼塊之中,分別用let和var聲明了兩個變量。然后在代碼塊之外調(diào)用這兩個變量,結(jié)果let聲明的變量報錯,var聲明的變量返回了正確的值。這表明,let聲明的變量只在它所在的代碼塊有效。
Swift:類似JavaScript,統(tǒng)一用var 聲明變量(語句結(jié)尾不用帶分號了)
var a = 10
var a = 1, b = 2, c = 3
與JavaScript不同,Swift是強(qiáng)類型的語言,但Swift可以根據(jù)賦值自動判斷出變量的類型!
當(dāng)你聲明常量或者變量的時候可以加上類型標(biāo)注(type annotation),說明常量或者變量中要存儲的值的類型。如果要添加類型標(biāo)注,需要在常量或者變量名后面加上一個冒號和空格,然后加上類型名稱。
這個例子給welcomeMessage變量添加了類型標(biāo)注,表示這個變量可以存儲String類型的值:
var welcomeMessage: String
聲明中的冒號代表著“是...類型”,所以這行代碼可以被理解為:“聲明一個類型為String,名字為welcomeMessage的變量。”“類型為String”的意思是“可以存儲任意String類型的值?!?br> welcomeMessage變量現(xiàn)在可以被設(shè)置成任意字符串:
welcomeMessage = "Hello"
你可以在一行中定義多個同樣類型的變量,用逗號分割,并在最后一個變量名之后添加類型標(biāo)注:
var red, green, blue: Double
常量的聲明和賦值
實踐中,有些數(shù)值我們不希望它的值變化(否則容易出錯),我們可以把它定義為常量(constant), 如C語言中
#define Pi 3.14
就把Pi固定為3.14,如果以后我們不小心對Pi重新賦值,編譯器就會報錯。這減少了出錯的可能。因此,一種編程語言應(yīng)該區(qū)分變量和常量。常量在程序運行過程中主要有2個作用:
- 代表常數(shù),便于程序的修改(例如:圓周率的值);
- 增強(qiáng)程序的可讀性(例如:常量UP、DOWN、LEFT和RIGHT分辨代表上下左右,其數(shù)值分別是1、2、3和4)。
C語言使用上述預(yù)處理命令來定義常量 。這在C語言中稱為“符號常量”,意思時它的主要用法在于用一個符號來替代一個值或文本。在程序中所有出現(xiàn)Pi的地方在編譯時都會被3.14所代替。
Java中,定義常量只需要在前面添加關(guān)鍵字final即可。在Java編碼規(guī)范中,要求常量名必須大寫。
則常量的語法格式如下:
final 數(shù)據(jù)類型 常量名稱 = 值;
final 數(shù)據(jù)類型 常量名稱1 = 值1, 常量名稱2 = 值2,……常量名稱n = 值n;
例如:
final double PI = 3.14;
final char MALE=‘M’,F(xiàn)EMALE=‘F’;
在Java語法中,常量也可以首先聲明,然后再進(jìn)行賦值,但是只能賦值一次,示例代碼如下:
final int UP;
UP = 1;
JavaScript:
ES6新增了const關(guān)鍵字用來聲明常量。一旦聲明,常量的值就不能改變。
'use strict';
const PI = 3.1415;
PI = 3;// TypeError: "PI" is read-only
上面代碼表明改變常量的值會報錯。注意,如果是常規(guī)模式,對常量賦值不會報錯,但也是無效的。
const PI = 3.1415;
PI = 3; // 常規(guī)模式時,重新賦值無效,但不報錯
const聲明的變量不得改變值,這意味著,const一旦聲明變量,就必須立即初始化,不能留到以后賦值。
Swift:使用let命令聲明常量并賦值
let a = 10
變量和常量的命名
變量不僅代表著我們的輸入,也代表著程序運行的各種狀態(tài)。因此,變量的名字非常重要。起名字往往是程序員最傷腦筋的一件事。清晰代表變量用途的名字讓程序一目了然,但太長又不好記不好寫。
名字并不是語法所規(guī)定的,但現(xiàn)代編程者趨向于按照共同的規(guī)則:用寧肯較長的有意義的名字,駝峰拼寫法;首字母小寫;常量大寫。
在Swift中,你可以用任何你喜歡的字符作為常量和變量名,包括 Unicode 字符:
let π = 3.14159
let 你好 = "你好世界"
let = "dogcow"
常量與變量名不能包含數(shù)學(xué)符號,箭頭,保留的(或者非法的)Unicode 碼位,連線與制表符。也不能以數(shù)字開頭,但是可以在常量與變量名的其他地方包含數(shù)字。當(dāng)然,最好還是使用英文字母作為變量和常量的名字。
綜上所述,Swift的var、let表示法最優(yōu)雅,JavaScript的var、const也還行。
作用域
變量的作用域是指哪個范圍內(nèi)變量的聲明有效。一般分為全局變量和局部變量。全局變量在整個源文件中有效,一般在函數(shù)外定義,又稱為外部變量。 外部變量可加強(qiáng)函數(shù)模塊之間的數(shù)據(jù)聯(lián)系(比如共用的數(shù)據(jù)),但是又使函數(shù)要依賴這些變量,因而使得函數(shù)的獨立性降低。從模塊化程序設(shè)計的觀點來看這是不利的,
因此在不必要時盡量不要使用全局變量。
局部變量也稱為內(nèi)部變量。局部變量是在函數(shù)內(nèi)作定義說明的。其作用域僅限于函數(shù)內(nèi), 離開該函數(shù)后再使用這種變量是非法的。
C語言明確區(qū)分全局變量和局部變量。在函數(shù)中聲明的都是局部變量,在函數(shù)外聲明的是全局變量(最好加extern)。但在函數(shù)內(nèi)使用外部變量時要加以說明。
int vs(int l,int w)
{
extern int h; //說明這里引用外部變量
int v;
v=l*w*h;
return v;
}
main()
{
extern int w,h;
int l=5;
printf("v=%d",vs(l,w));
}
int l=3,w=4,h=5;
Java:
大多數(shù)其他計算機(jī)語言定義了兩大類作用域:全局和局部。然而,這些傳統(tǒng)型的作用域不適合Java 的嚴(yán)格的面向?qū)ο蟮哪P?。?dāng)然將一個變量定義為全局變量是可行的,但這是例外而不是規(guī)則。在Java 中2個主要的作用域是通過類和方法定義的。盡管類的作用域和方法的作用域的區(qū)別有點人為劃定。因為類的作用域有若干獨特的特點和屬性,而且這些特點和屬性不能應(yīng)用到方法定義的作用域,這些差別還是很有意義的。因為有差別,類(以及在其內(nèi)定義的變量)的作用域?qū)⒈煌七t到第6章當(dāng)討論類時再來討論。到現(xiàn)在為止,我們將僅僅考慮由方法或在一個方法內(nèi)定義的作用域。
方法定義的作用域以它的左大括號開始。但是,如果該方法有參數(shù),那么它們也被包括在該方法的作用域中。可認(rèn)為它們與方法中其他變量的作用域一樣。
作為一個通用規(guī)則,在一個作用域中定義的變量對于該作用域外的程序是不可見(即訪問)的。因此,當(dāng)你在一個作用域中定義一個變量時,你就將該變量局部化并且保護(hù)它不被非授權(quán)訪問和/或修改。實際上,作用域規(guī)則為封裝提供了基礎(chǔ)。
作用域可以進(jìn)行嵌套。例如每次當(dāng)你創(chuàng)建一個程序塊,你就創(chuàng)建了一個新的嵌套的作用域。這樣,外面的作用域包含內(nèi)部的作用域。這意味著外部作用域定義的對象對于內(nèi)部作用域中的程序是可見的。但是,反過來就是錯誤的。內(nèi)部作用域定義的對象對于外部是不可見的。
為理解嵌套作用域的效果,考慮下面的程序:
// Demonstrate block scope.
class Scope {
public static void main(String args[])
{
int x; // known to all code within main
x = 10;
if(x == 10)
{ // start new scope
int y = 20; // known only to this block
// x and y both known here.
System.out.println("x and y: " + x + " " + y);
x = y * 2;
}
// y = 100; // Error! y not known here
// x is still known here.
System.out.println("x is " + x);
}
}
正如注釋中說明的那樣,在方法main() 的開始定義了變量x,因此它對于main() 中的所有的隨后的代碼都是可見的。在if程序塊中定義了變量y。因為一個塊定義一個作用域,y 僅僅對在它的塊以內(nèi)的其他代碼可見。這就是在它的塊之外的程序行y=100; 被注釋掉的原因。如果你將該行前面的注釋符號去掉,編譯程序時就會出現(xiàn)錯誤,因為變量y在它的程序塊之外是不可見的。在if程序塊中可以使用變量x,因為塊(即一個嵌套作用域)中的程序可以訪問被其包圍作用域中定義的變量。
另一個需要記住的重要之處是:變量在其作用域內(nèi)被創(chuàng)建,離開其作用域時被撤消。
這意味著一個變量一旦離開它的作用域,將不再保存它的值了。因此,在一個方法內(nèi)定義的變量在幾次調(diào)用該方法之間將不再保存它們的值。同樣,在塊內(nèi)定義的變量在離開該塊時也將丟棄它的值。因此,一個變量的生存期就被限定在它的作用域中。
如果一個聲明定義包括一個初始化,那么每次進(jìn)入聲明它的程序塊時,該變量都要被重新初始化。例如,考慮這個程序:
// Demonstrate lifetime of a variable.
class LifeTime {
public static void main(String args[])
{
int x;
for(x = 0; x < 3; x++)
{
int y = -1; // y is initialized each time block is enter
System.out.println("y is: " + y); // this always prints -1
y = 100;
System.out.println("y is now: " + y);
}
}
}
該程序運行的輸出如下:
y is: -1
y is now: 100
y is: -1
y is now: 100
y is: -1
y is now: 100
可以看到,每次進(jìn)入內(nèi)部的for循環(huán),y都要被重新初始化為-1。即使它隨后被賦值為100,該值還是被丟棄了。
最后一點:盡管程序塊能被嵌套,你不能將內(nèi)部作用域聲明的變量與其外部作用域聲明的變量重名。在這一點上,Java 不同于C和C++。
JavaScript:
傳統(tǒng)的JS語法中最被人詬病的一點就是依賴于全局變量,在聲明局部變量是一定要使用var語句,否則會視為對全局變量的引用。看下面代碼:
/* 錯誤修改了全局變量 */
var scope = "global";
function checkScope(){
scope = "local";
document.write(scope);
}
checkScope(); //輸出"local"
document.write(scope); //輸出"local"
Javascript沒有塊級作用域,函數(shù)中聲明的變量在整個函數(shù)中都是有定義的。有可能導(dǎo)致內(nèi)層變量覆蓋外層變量:
/* 變量提升 */
var scope = "global";
function checkScope() {
document.write(scope); //語句1
var scope = "local"; //語句2
document.write(scope); //語句3
}
checkScope(); //輸出"undefined 、local"
如果沒有語句2和3,語句1將輸出“global”。由于語句2(var scope = "local";)聲明的變量在整個checkScope函數(shù)作用域內(nèi)都有效,因此在語句1(**document.write(scope); **)執(zhí)行的時scope引用的是局部變量,而此時局部變量scope尚未定義,所以輸出”undefined”。因此一個好的編程習(xí)慣是將所有的變量聲明集中起來放在函數(shù)的開頭。
ES5只有全局作用域和函數(shù)作用域,沒有塊級作用域,這帶來很多不合理的場景。第一種場景,內(nèi)層變量可能會覆蓋外層變量。如上面的例子。第二種場景,用來計數(shù)的循環(huán)變量泄露為全局變量。
var s = 'hello';
for (var i = 0; i < s.length; i++){
console.log(s[i]);
}
console.log(i); // 5
上面代碼中,變量i只用來控制循環(huán),但是循環(huán)結(jié)束后,它并沒有消失,泄露成了全局變量。
ES6新增的let命令實際上為JavaScript新增了塊級作用域。只要用花括號括起的代碼段就是一個塊級作用域。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
上面代碼在代碼塊之中,分別用let和var聲明了兩個變量。然后在代碼塊之外調(diào)用這兩個變量,結(jié)果let聲明的變量報錯,var聲明的變量返回了正確的值。這表明,let聲明的變量只在它所在的代碼塊有效。
for循環(huán)的計數(shù)器,就很合適使用let命令。
for(let i = 0; i < arr.length; i++){}
console.log(i)//ReferenceError: i is not defined
上面代碼的計數(shù)器i,只在for循環(huán)體內(nèi)有效。
ES6允許塊級作用域的任意嵌套。
內(nèi)層作用域可以定義外層作用域的同名變量。
{{{{ let insane = 'Hello World';
{let insane = 'Hello World';}
}}}};
const的作用域與let命令相同:只在聲明所在的塊級作用域內(nèi)有效。
因此,使用ES6,最好用let聲明變量。
數(shù)據(jù)類型
從C語言開始,為了節(jié)約空間和便于管理,把存儲數(shù)據(jù)的單元規(guī)范為幾種“數(shù)據(jù)類型”。每種數(shù)據(jù)類型都有一些相對應(yīng)的操作。如果一種類型的數(shù)據(jù)要執(zhí)行另外類型的操作,比如,將數(shù)字連接在一起,就要先進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換。數(shù)據(jù)類型則指明了數(shù)據(jù)的長度和處理方式。所以諸如int n、char c、float money這樣的形式就確定了數(shù)據(jù)在內(nèi)存中的所有要素。C語言提供的多種數(shù)據(jù)類型讓程序更加靈活和高效,同時也增加了學(xué)習(xí)成本。而有些編程語言,例如PHP、JavaScript等,在定義變量時不需要指明數(shù)據(jù)類型,編譯器會根據(jù)賦值情況自動推演出數(shù)據(jù)類型,更加智能。除了C語言,Java、C++、C#等在定義變量時也必須指明數(shù)據(jù)類型,這樣的編程語言稱為強(qiáng)類型語言。而PHP、JavaScript等在定義變量時不必指明數(shù)據(jù)類型,編譯系統(tǒng)會自動推演,這樣的編程語言稱為弱類型語言。強(qiáng)類型語言一旦確定了數(shù)據(jù)類型,就不能再賦給其他類型的數(shù)據(jù),除非對數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換。弱類型語言沒有這種限制,一個變量,可以先賦給一個整數(shù),然后再賦給一個字符串。最后需要說明的是:數(shù)據(jù)類型只在定義變量時指明,而且必須指明;使用變量時無需再指明,因為此時的數(shù)據(jù)類型已經(jīng)確定了。
那么,到底強(qiáng)類型好還是弱類型好呢?強(qiáng)類型語言安全性高,程序執(zhí)行效率高,節(jié)省內(nèi)存空間,但編碼是需指明類型和進(jìn)行轉(zhuǎn)換。弱類型編碼方便,但需要編譯器判斷,執(zhí)行效率低些,內(nèi)存占用多。從“盡量讓機(jī)器干活”的角度,還是弱類型好些。
C語言的基本數(shù)據(jù)類型###
** 整數(shù)
計算機(jī)最基本的工作是處理數(shù)字和數(shù)學(xué)計算。整數(shù)的處理是其中最基本的。從c時代起,整數(shù)的表示和處理就有一套規(guī)范。
C語言中有三種整數(shù)類型,分別為 short、int 和 long。int 稱為整型,short 稱為短整型,long 稱為長整型,它們的長度(所占字節(jié)數(shù))關(guān)系為:short <= int <= long它們具體占用幾個字節(jié)C語言并沒有規(guī)定,C語言只做了寬泛的限制:short 至少占用2個字節(jié)。int 建議為一個機(jī)器字長。32位環(huán)境下機(jī)器字長為4字節(jié),64位環(huán)境下機(jī)器字長為8字節(jié)。short 的長度不能大于 int,long 的長度不能小于 int。這就意味著,short 并不一定真的”短“,long 也并不一定真的”長“,它們有可能和 int 占用相同的字節(jié)數(shù)。決定整數(shù)長度的因素很多,包括硬件(CPU和數(shù)據(jù)總線)、操作系統(tǒng)、編譯器等。在16位環(huán)境下,short 為2個字節(jié),int 為2個字節(jié),long 為4個字節(jié)。16位環(huán)境多用于單片機(jī)和低級嵌入式系統(tǒng),在PC和服務(wù)器上基本都看不到了。對于32位的 Windows、Linux 和 OS X,short 為2個字節(jié),int 為4個字節(jié),long 也為4個字節(jié)。PC和服務(wù)器上的32位系統(tǒng)占有率也在慢慢下降,嵌入式系統(tǒng)使用32位越來越多。在64位環(huán)境下,不同的操作系統(tǒng)會有不同的結(jié)果,如下所示(長度以字節(jié)計):
操作系統(tǒng) short int long
Win64 2 4 4
類Unix系統(tǒng)(包括 Unix、Linux、OS X、BSD、Solaris 等) 2 4 8
獲取某個數(shù)據(jù)類型的長度可以使用 sizeof 操作符
符號位
在數(shù)學(xué)中,數(shù)字有正負(fù)之分。在C語言中也是一樣,short、int、long 都可以帶上符號,例如:
short a = -10; //負(fù)數(shù)
int b = +10; //正數(shù)
long c = (-9) + (+12); //負(fù)數(shù)和正數(shù)相加
如果不帶正負(fù)號,默認(rèn)就是正數(shù)。
在符號位中,用0表示正數(shù),用1表示負(fù)數(shù)。例如 short 類型的 -10、+16 在內(nèi)存中的表示如下:

如果不希望設(shè)置符號位,可以在數(shù)據(jù)類型前面加 unsigned,如下所示:unsigned short a = 12;
unsigned int b = 1002;
unsigned long c = 9892320;
這樣,short、int、long 中就沒有符號位了,所有的位都用來表示數(shù)值。也就意味著,使用了 unsigned 只能表示正數(shù),不能表示負(fù)數(shù)了。
取值范圍和數(shù)據(jù)溢出
short、int、long 占用的字節(jié)數(shù)不同,所能表示的數(shù)值范圍也不同。以32位平臺為例,下面是它們的取值范圍:
| 數(shù)據(jù)類型 | 所占字節(jié) | 取值范圍 |
|---|---|---|
| short | 2 | -32768~32767 |
| unsigned short | 2 | 0~65535 |
| int | 4 | -2147483648~2147483647(±21億) |
| unsigned int | 4 | 0~4294967295(42億) |
| long | 4 | -2147483648~2147483647 |
| unsigned long | 4 | 0~4294967295 |
當(dāng)數(shù)值過大或過小時,有限的幾個字節(jié)就不能表示,就會發(fā)生溢出。發(fā)生溢出時,最高位會被截去。請看下面的例子:
#include <stdio.h>
int main()
{
unsigned int a = 0x100000000;
printf("a=%u\n", a);
return 0;
}
運行結(jié)果:a=0變量 a 為 int 類型,占用4個字節(jié)(32位),能表示的最大值為 0xFFFFFFFF,而 0x100000000 = 0xFFFFFFFF + 1,占用33位,已超出 a 所能表示的最大值,會發(fā)生溢出,最高位被截去,剩下的32位都是0。也就是說,在 a 被輸出前,其值已經(jīng)變成了 0。
各種整數(shù)的輸出
在使用 printf 輸出整數(shù)時,不同的控制字符會有不同的輸出格式。1) 輸出 int 使用%d,輸出 short 使用 %hd,輸出 long 使用 %ld。2) 輸出無符號數(shù)使用%u。3) 輸出十進(jìn)制使用%d,輸出八進(jìn)制使用%o,輸出十六進(jìn)制使用%x或%X。如果希望帶上前綴,可以加#,例如 %#d、%#o、%#x、%#X。
小數(shù)
在計算機(jī)中表示小數(shù)是一件很麻煩的事,這是二進(jìn)制本身的特點決定的。C語言證明了這一點。
C語言中小數(shù)(又稱為浮點數(shù))的數(shù)據(jù)類型為 float 或 double:float 稱為單精度浮點數(shù),double 稱為雙精度浮點數(shù)。不像整數(shù),小數(shù)的長度始終是固定的,float 占用4個字節(jié),double 占用8個字節(jié)。
C語言在內(nèi)部采用指數(shù)形式表示小數(shù)。正負(fù)號、指數(shù)(n)、尾數(shù)(a) 是變化的,需要占用內(nèi)存空間來表示。float、double 在內(nèi)存中的形式如下所示:

輸出 float 使用 %f 控制符,輸出 double 使用 %lf 控制符。
#include <stdio.h>
#include <stdlib.h>
int main()
{
float a=128.101;
float b=0.302f;
float c=1.23002398f;
double d=123;
double e = 78.429;
printf("a=%f \nb=%f \nc=%f \nd=%lf \ne=%lf\n", a, b, c, d, e);
system("pause");
return 0;
}
對代碼的說明:1) %f 默認(rèn)保留六位小數(shù),不足六位以 0 補(bǔ)齊,超過六位按四舍五入截斷。2) 將整數(shù)賦值給 float 變量時會轉(zhuǎn)換為小數(shù)。3) 小數(shù)默認(rèn)為 double 類型,加上后綴f才是float類型。4) 由于內(nèi)存有限,小數(shù)的精度受限,所以輸出 a 時只能獲得一個近似數(shù)。
字符和字符串
在人機(jī)交互的方式中,自然語言無疑是最自然的方式,而通過文本交互又是使用自然語言與機(jī)器對話的最基本形式,這就涉及字符串的處理。因此,對任何編程語言,對字符串的處理是非常重要的。
恰恰,字符串在機(jī)器中的表示和處理又是非常復(fù)雜的一件事。人類語言使用了多種多樣的字符(包括標(biāo)點符號),字符串本身的長度又不固定,字符串的操作又很多樣,所以對編程語言來說,處理好字符串是很考驗功力的。
在C語言中,根本就沒有字符串這種數(shù)據(jù)類型。C語言中只有字符(char)這種數(shù)據(jù)類型,占用一個字節(jié),可以存放本地字符集(一般就是ASCII字符集)中的一個字符,表示為char c = 'a'。char 變量在內(nèi)存中存儲的是字符對應(yīng)的 ASCII 碼值。如果以 %c 輸出,會根據(jù) ASCII 碼表轉(zhuǎn)換成對應(yīng)的字符;如果以 %d 輸出,那么還是整數(shù)。C語言還定義了一些轉(zhuǎn)義字符,如,用\n表示換行符,用\r表示回車符等。
C語言標(biāo)準(zhǔn)庫提供的輸入輸出模型非常簡單,都是按照字符流的方式處理,每次讀/寫一個字符,如getchar和putchar函數(shù)。C語言將字符串看做字符數(shù)組,用數(shù)組的線性存儲模式存儲字符串,除了字符,最后還要存一個\n。為了用變量表示字符串,可以用數(shù)組的指針。可以借助下面的形式將字符串賦值給變量:
char variableName = "string";
char和是固定的形式,variableNmae 為變量名稱,"string" 是要賦值的字符串。
字符串使用示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char c = '@';
char *str = "This is a string.";
printf("char: %c\n", c);
printf("string1: %s\n", str);
//也可以直接輸出字符串
printf("string2: %s\n", "This is another string.");
system("pause");
return 0;
}
運行結(jié)果:char: @string1: This is a string.string2: This is another string.
C89標(biāo)準(zhǔn)中沒有定義布爾類型;C99中增加了_Bool類型。
_Bool型變量實際上只是一個整數(shù)類型,
但是,_Bool只能被賦值為0或1,凡是不為0的整數(shù)都被轉(zhuǎn)變?yōu)?。
C語言中還有一些復(fù)合類型,如數(shù)組、結(jié)構(gòu)、聯(lián)合等,不作為基本數(shù)據(jù)類型在這里描述。
JAVA的基本數(shù)據(jù)類型###
Java的基本數(shù)據(jù)類型就是內(nèi)置數(shù)據(jù)類型(除此之外還有引用數(shù)據(jù)類型)。
Java語言提供了八種基本類型。六種數(shù)字類型(四個整數(shù)型,兩個浮點型),一種字符類型,還有一種布爾型。
byte:
byte數(shù)據(jù)類型是8位、有符號的,以二進(jìn)制補(bǔ)碼表示的整數(shù);
最小值是-128(-2^7);
最大值是127(2^7-1);
默認(rèn)值是0;
byte類型用在大型數(shù)組中節(jié)約空間,主要代替整數(shù),因為byte變量占用的空間只有int類型的四分之一;
例子:byte a = 100,byte b = -50。short:
short數(shù)據(jù)類型是16位、有符號的以二進(jìn)制補(bǔ)碼表示的整數(shù)
最小值是-32768(-2^15);
最大值是32767(2^15 - 1);
Short數(shù)據(jù)類型也可以像byte那樣節(jié)省空間。一個short變量是int型變量所占空間的二分之一;
默認(rèn)值是0;
例子:short s = 1000,short r = -20000。int:
int數(shù)據(jù)類型是32位、有符號的以二進(jìn)制補(bǔ)碼表示的整數(shù);
最小值是-2,147,483,648(-2^31);
最大值是2,147,485,647(2^31 - 1);
一般地整型變量默認(rèn)為int類型;
默認(rèn)值是0;
例子:int a = 100000, int b = -200000。long:
long數(shù)據(jù)類型是64位、有符號的以二進(jìn)制補(bǔ)碼表示的整數(shù);
最小值是-9,223,372,036,854,775,808(-2^63);
最大值是9,223,372,036,854,775,807(2^63 -1);
這種類型主要使用在需要比較大整數(shù)的系統(tǒng)上;
默認(rèn)值是0L;
例子: long a = 100000L,Long b = -200000L。float:
float數(shù)據(jù)類型是單精度、32位、符合IEEE 754標(biāo)準(zhǔn)的浮點數(shù);
float在儲存大型浮點數(shù)組的時候可節(jié)省內(nèi)存空間;
默認(rèn)值是0.0f;
浮點數(shù)不能用來表示精確的值,如貨幣;
例子:float f1 = 234.5f。
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38double:
double數(shù)據(jù)類型是雙精度、64位、符合IEEE 754標(biāo)準(zhǔn)的浮點數(shù);
浮點數(shù)的默認(rèn)類型為double類型;
double類型同樣不能表示精確的值,如貨幣;
默認(rèn)值是0.0d;
例子:double d1 = 123.4。
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
Float和Double的最小值和最大值都是以科學(xué)記數(shù)法的形式輸出的,結(jié)尾的"E+數(shù)字"表示E之前的數(shù)字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。boolean:Java中引入了布爾類型。
boolean數(shù)據(jù)類型表示一位的信息;
只有兩個取值:true和false;
這種類型只作為一種標(biāo)志來記錄true/false情況;
默認(rèn)值是false;
例子:boolean one = true。char:
char類型是一個單一的16位Unicode字符;
最小值是’\u0000’(即為0);
最大值是’\uffff’(即為65,535);
char數(shù)據(jù)類型可以儲存任何字符;
例子:char letter = ‘A’。
在Java中字符串屬于對象,Java提供了String類來創(chuàng)建和操作字符串。
總的來講,Java的數(shù)據(jù)類型比較齊整嚴(yán)謹(jǐn),支持的數(shù)值范圍也足夠大。
實際上,JAVA中還存在另外一種基本類型void,它也有對應(yīng)的包裝類 java.lang.Void,不過我們無法直接對它們進(jìn)行操作。
JavaScript的數(shù)據(jù)類型###
作為弱類型語言的代表,JavaScript不強(qiáng)調(diào)變量的“類型”,實際上,你可以隨時給變量賦予任何類型:無論數(shù)字、字母還是字符串、布爾值。
var x // x 為 undefined
x = 6; // x 為數(shù)字
x = "Bill"; // x 為字符串
x = 34.00; //小數(shù)
x =123e-5; // 0.00123
x = true
事實上,JavaScript是使用了對象表示一切: Number 對象表示數(shù)字。String(字符串)對象表示字符和字符串。每種對象有不同的方法,所以有時還要用Number()、 toString()等方法轉(zhuǎn)換。
數(shù)字對象
JavaScript 只有一種數(shù)字類型。
可以使用也可以不使用小數(shù)點來書寫數(shù)字。
實例
var pi=3.14; // 使用小數(shù)點var x=34; // 不使用小數(shù)點
極大或極小的數(shù)字可通過科學(xué)(指數(shù))計數(shù)法來寫:
實例
var y=123e5; // 12300000var z=123e-5; // 0.00123
所有 JavaScript 數(shù)字均為 64 位
JavaScript 不是類型語言。與許多其他編程語言不同,JavaScript 不定義不同類型的數(shù)字,比如整數(shù)、短、長、浮點等等。
JavaScript 中的所有數(shù)字都存儲為根為 10 的 64 位(8 比特),浮點數(shù)。
精度
整數(shù)(不使用小數(shù)點或指數(shù)計數(shù)法)最多為 15 位(十兆級)。
小數(shù)的最大位數(shù)是 17,但是浮點運算并不總是 100% 準(zhǔn)確:
實例
0.2 + 0.1 = 0.30000000000000004
八進(jìn)制和十六進(jìn)制
如果前綴為 0,則 JavaScript 會把數(shù)值常量解釋為八進(jìn)制數(shù),如果前綴為 0 和 "x",則解釋為十六進(jìn)制數(shù)。
實例
var y=0377;var z=0xFF;
提示:絕不要在數(shù)字前面寫零,除非您需要進(jìn)行八進(jìn)制轉(zhuǎn)換。
數(shù)字屬性和方法
屬性:
MAX VALUE
MIN VALUE
NEGATIVE INFINITIVE
POSITIVE INFINITIVE
NaN
prototype
constructor
方法:
toExponential()
toFixed()
toPrecision()
toString()
valueOf()
字符串(String) 對象描述
字符串是 JavaScript 的一種基本的數(shù)據(jù)類型。
String 對象的 length 屬性聲明了該字符串中的字符數(shù)。
String 類定義了大量操作字符串的方法,例如從字符串中提取字符或子串,或者檢索字符或子串。
需要注意的是,JavaScript 的字符串是不可變的(immutable),String 類定義的方法都不能改變字符串的內(nèi)容。像 String.toUpperCase() 這樣的方法,返回的是全新的字符串,而不是修改原始字符串。
在較早的 Netscape 代碼基的 JavaScript 實現(xiàn)中(例如 Firefox 實現(xiàn)中),字符串的行為就像只讀的字符數(shù)組。例如,從字符串 s 中提取第三個字符,可以用 s[2] 代替更加標(biāo)準(zhǔn)的 s.charAt(2)。此外,對字符串應(yīng)用 for/in 循環(huán)時,它將枚舉字符串中每個字符的數(shù)組下標(biāo)(但要注意,ECMAScript 標(biāo)準(zhǔn)規(guī)定,不能枚舉 length 屬性)。因為字符串的數(shù)組行為不標(biāo)準(zhǔn),所以應(yīng)該避免使用它。
ES6加強(qiáng)了對Unicode的支持,并且擴(kuò)展了字符串對象。
ES6提供了二進(jìn)制和八進(jìn)制數(shù)值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示。
0b111110111 === 503 // true
0o767 === 503 // true
ES6在Number對象上,新提供了Number.isFinite()和Number.isNaN()兩個方法,用來檢查Infinite和NaN這兩個特殊值。
Number.EPSILON
ES6在Number對象上面,新增一個極小的常量Number.EPSILON
。
Number.EPSILON// 2.220446049250313e-16Number.EPSILON.toFixed(20)// '0.00000000000000022204'
引入一個這么小的量的目的,在于為浮點數(shù)計算,設(shè)置一個誤差范圍。我們知道浮點數(shù)計算是不精確的。
0.1 + 0.2// 0.300000000000000040.1 + 0.2 - 0.3// 5.551115123125783e-175.551115123125783e-17.toFixed(20)// '0.00000000000000005551'
但是如果這個誤差能夠小于Number.EPSILON
,我們就可以認(rèn)為得到了正確結(jié)果。
5.551115123125783e-17 < Number.EPSILON// true
因此,Number.EPSILON
的實質(zhì)是一個可以接受的誤差范圍。
function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON}withinErrorMargin(0.1 + 0.2, 0.3)// truewithinErrorMargin(0.2 + 0.2, 0.3)// false
上面的代碼為浮點數(shù)運算,部署了一個誤差檢查函數(shù)。
JavaScript能夠準(zhǔn)確表示的整數(shù)范圍在-253到253之間(不含兩個端點),超過這個范圍,無法精確表示這個值。
Math.pow(2, 53) // 90071992547409929007199254740992 // 90071992547409929007199254740993 // 9007199254740992Math.pow(2, 53) === Math.pow(2, 53) + 1// true
上面代碼中,超出2的53次方之后,一個數(shù)就不精確了。
ES6引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
這兩個常量,用來表示這個范圍的上下限。這可以用來預(yù)先檢查參與運算的數(shù)值,避免超出范圍。
Swift的基本數(shù)據(jù)類型###
類型標(biāo)注
我們知道,Swift中用var聲明變量,用let聲明常量,如果賦給初始值,系統(tǒng)會自動判斷出數(shù)據(jù)類型。
如果開始不便賦初始值,當(dāng)你聲明常量或者變量的時候可以加上類型標(biāo)注(type annotation),說明常量或者變量中要存儲的值的類型。如果要添加類型標(biāo)注,需要在常量或者變量名后面加上一個冒號和空格,然后加上類型名稱。
這個例子給welcomeMessage變量添加了類型標(biāo)注,表示這個變量可以存儲String類型的值:
var welcomeMessage: String
聲明中的冒號代表著“是...類型”,所以這行代碼可以被理解為:
“聲明一個類型為String,名字為welcomeMessage的變量?!?br>
“類型為String”的意思是“可以存儲任意String類型的值。”
變量現(xiàn)在可以被設(shè)置成任意字符串:
welcomeMessage = "Hello"
你可以在一行中定義多個同樣類型的變量,用逗號分割,并在最后一個變量名之后添加類型標(biāo)注:
var red, green, blue: Double
注意:一般來說你很少需要寫類型標(biāo)注。如果你在聲明常量或者變量的時候賦了一個初始值,Swift可以推斷出這個常量或者變量的類型。在上面的例子中,沒有給welcomeMessage賦初始值,所以變量welcomeMessage的類型是通過一個類型標(biāo)注指定的,而不是通過初始值推斷的。
類型安全和類型推斷
Swift 是一個類型安全(type safe)的語言。類型安全的語言可以讓你清楚地知道代碼要處理的值的類型。如果你的代碼需要一個String
,你絕對不可能不小心傳進(jìn)去一個Int
。
由于 Swift 是類型安全的,所以它會在編譯你的代碼時進(jìn)行類型檢查(type checks),并把不匹配的類型標(biāo)記為錯誤。這可以讓你在開發(fā)的時候盡早發(fā)現(xiàn)并修復(fù)錯誤。
當(dāng)你要處理不同類型的值時,類型檢查可以幫你避免錯誤。然而,這并不是說你每次聲明常量和變量的時候都需要顯式指定類型。如果你沒有顯式指定類型,Swift 會使用類型推斷(type inference)來選擇合適的類型。有了類型推斷,編譯器可以在編譯代碼的時候自動推斷出表達(dá)式的類型。原理很簡單,只要檢查你賦的值即可。
因為有類型推斷,和 C 或者 Objective-C 比起來 Swift 很少需要聲明類型。常量和變量雖然需要明確類型,但是大部分工作并不需要你自己來完成。
當(dāng)你聲明常量或者變量并賦初值的時候類型推斷非常有用。當(dāng)你在聲明常量或者變量的時候賦給它們一個字面量(literal value 或 literal)即可觸發(fā)類型推斷。(字面量就是會直接出現(xiàn)在你代碼中的值,比如42和3.14159。)
例如,如果你給一個新常量賦值42并且沒有標(biāo)明類型,Swift 可以推斷出常量類型是Int,因為你給它賦的初始值看起來像一個整數(shù):
let meaningOfLife = 42// meaningOfLife 會被推測為 Int 類型
同理,如果你沒有給浮點字面量標(biāo)明類型,Swift 會推斷你想要的是Double
:
let pi = 3.14159// pi 會被推測為 Double 類型
當(dāng)推斷浮點數(shù)的類型時,Swift 總是會選擇Double而不是Float。如果表達(dá)式中同時出現(xiàn)了整數(shù)和浮點數(shù),會被推斷為Double類型:
let anotherPi = 3 + 0.14159// anotherPi 會被推測為 Double 類型
沒有顯式聲明類型,而表達(dá)式中出現(xiàn)了一個浮點字面量,所以表達(dá)式會被推斷為Double類型。
幾種數(shù)值類型
- 整數(shù)
整數(shù)就是沒有小數(shù)部分的數(shù)字,比如42和-23。整數(shù)可以是有符號
(正、負(fù)、零)或者無符號(正、零)。
Swift 提供了8,16,32和64位的有符號和無符號整數(shù)類型。這些整數(shù)類型和 C 語言的命名方式很像,比如8位無符號整數(shù)類型是UInt8,32位有符號整數(shù)類型是Int32。就像 Swift 的其他類型一樣,整數(shù)類型采用大寫命名法。
整數(shù)范圍
你可以訪問不同整數(shù)類型的min和max
屬性來獲取對應(yīng)類型的最小值和最大值:
let minValue = UInt8.min // minValue 為 0,是 UInt8 類型
let maxValue = UInt8.max // maxValue 為 255,是 UInt8 類型
min和max所傳回值的類型,正是其所對的整數(shù)類型(如上例UInt8, 所傳回的類型是UInt8),可用在表達(dá)式中相同類型值旁。
Int
一般來說,你不需要專門指定整數(shù)的長度。Swift 提供了一個特殊的整數(shù)類型Int
,長度與當(dāng)前平臺的原生字長相同:在32位平臺上,Int和Int32
長度相同。在64位平臺上,Int和Int64長度相同。
除非你需要特定長度的整數(shù),一般來說使用Int
就夠了。這可以提高代碼一致性和可復(fù)用性。即使是在32位平臺上,Int
可以存儲的整數(shù)范圍也可以達(dá)到-2,147,483,648~2,147,483,647,大多數(shù)時候這已經(jīng)足夠大了。
UInt
Swift 也提供了一個特殊的無符號類型UInt,長度與當(dāng)前平臺的原生字長相同:
在32位平臺上,UInt和UInt32長度相同。在64位平臺上,UInt和UInt64長度相同。
注意:盡量不要使用UInt,除非你真的需要存儲一個和當(dāng)前平臺原生字長相同的無符號整數(shù)。除了這種情況,最好使用Int,即使你要存儲的值已知是非負(fù)的。統(tǒng)一使用Int可以提高代碼的可復(fù)用性,避免不同類型數(shù)字之間的轉(zhuǎn)換,并且匹配數(shù)字的類型推斷。
- 浮點數(shù)
浮點數(shù)是有小數(shù)部分的數(shù)字,比如3.14159,0.1和-273.15。
浮點類型比整數(shù)類型表示的范圍更大,可以存儲比Int
類型更大或者更小的數(shù)字。Swift 提供了兩種有符號浮點數(shù)類型:
Double
表示64位浮點數(shù)。當(dāng)你需要存儲很大或者很高精度的浮點數(shù)時請使用此類型。
Float
表示32位浮點數(shù)。精度要求不高的話可以使用此類型。
注意:Double精確度很高,至少有15位數(shù)字,而Float只有6位數(shù)字。選擇哪個類型取決于你的代碼需要處理的值的范圍。
其他進(jìn)制的表示
整數(shù)字面量可以被寫作:
一個十進(jìn)制數(shù),沒有前綴
一個二進(jìn)制數(shù),前綴是0b
一個八進(jìn)制數(shù),前綴是0o
一個十六進(jìn)制數(shù),前綴是0x
下面的所有整數(shù)字面量的十進(jìn)制值都是17:
let decimalInteger = 17
let binaryInteger = 0b10001 // 二進(jìn)制的17
let octalInteger = 0o21 // 八進(jìn)制的17
let hexadecimalInteger = 0x11 // 十六進(jìn)制的17
浮點字面量可以是十進(jìn)制(沒有前綴)或者是十六進(jìn)制(前綴是0x)。小數(shù)點兩邊必須有至少一個十進(jìn)制數(shù)字(或者是十六進(jìn)制的數(shù)字)。十進(jìn)制浮點數(shù)也可以有一個可選的指數(shù)(exponent),通過大寫或者小寫的 e來指定;十六進(jìn)制浮點數(shù)必須有一個指數(shù),通過大寫或者小寫的 p來指定。
如果一個十進(jìn)制數(shù)的指數(shù)為exp,那這個數(shù)相當(dāng)于基數(shù)和10^exp的乘積:
1.25e2表示 1.25 × 10^2,等于 125.0。
1.25e-2表示 1.25 × 10^-2,等于 0.0125。
如果一個十六進(jìn)制數(shù)的指數(shù)為exp,那這個數(shù)相當(dāng)于基數(shù)和2^exp的乘積:
0xFp2表示 15 × 2^2,等于 60.0。0xFp-2表示 15 × 2^-2,等于 3.75。
下面的這些浮點字面量都等于十進(jìn)制的12.1875:
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
數(shù)值類字面量可以包括額外的格式來增強(qiáng)可讀性。整數(shù)和浮點數(shù)都可以添加額外的零并且包含下劃線,并不會影響字面量:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
數(shù)值型類型轉(zhuǎn)換
通常來講,即使代碼中的整數(shù)常量和變量已知非負(fù),也請使用Int類型。總是使用默認(rèn)的整數(shù)類型可以保證你的整數(shù)常量和變量可以直接被復(fù)用并且可以匹配整數(shù)類字面量的類型推斷。只有在必要的時候才使用其他整數(shù)類型,比如要處理外部的長度明確的數(shù)據(jù)或者為了優(yōu)化性能、內(nèi)存占用等等。使用顯式指定長度的類型可以及時發(fā)現(xiàn)值溢出并且可以暗示正在處理特殊數(shù)據(jù)。
整數(shù)轉(zhuǎn)換
不同整數(shù)類型的變量和常量可以存儲不同范圍的數(shù)字。Int8
類型的常量或者變量可以存儲的數(shù)字范圍是-128127,而UInt8類型的常量或者變量能存儲的數(shù)字范圍是0255。如果數(shù)字超出了常量或者變量可存儲的范圍,編譯的時候會報錯:
let cannotBeNegative: UInt8 = -1// UInt8 類型不能存儲負(fù)數(shù),所以會報錯
let tooBig: Int8 = Int8.max + 1// Int8 類型不能存儲超過最大值的數(shù),所以會報錯
由于每種整數(shù)類型都可以存儲不同范圍的值,所以你必須根據(jù)不同情況選擇性使用數(shù)值型類型轉(zhuǎn)換。這種選擇性使用的方式,可以預(yù)防隱式轉(zhuǎn)換的錯誤并讓你的代碼中的類型轉(zhuǎn)換意圖變得清晰。
要將一種數(shù)字類型轉(zhuǎn)換成另一種,你要用當(dāng)前值來初始化一個期望類型的新數(shù)字,這個數(shù)字的類型就是你的目標(biāo)類型。在下面的例子中,常量twoThousand
是UInt16類型,然而常量one是UInt8類型。它們不能直接相加,因為它們類型不同。所以要調(diào)用UInt16(one)來創(chuàng)建一個新的UInt16數(shù)字并用one
的值來初始化,然后使用這個新數(shù)字來計算:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
現(xiàn)在兩個數(shù)字的類型都是UInt16,可以進(jìn)行相加。目標(biāo)常twoThousandAndOne
的類型被推斷為UInt16,因為它是兩個UInt16值的和。
SomeType(ofInitialValue)
是調(diào)用 Swift 構(gòu)造器并傳入一個初始值的默認(rèn)方法。在語言內(nèi)部,UInt16
有一個構(gòu)造器,可以接受一個UInt8類型的值,所以這個構(gòu)造器可以用現(xiàn)有UInt8來創(chuàng)建一個新的UInt16。注意,你并不能傳入任意類型的值,只能傳入UInt16
內(nèi)部有對應(yīng)構(gòu)造器的值。不過你可以擴(kuò)展現(xiàn)有的類型來讓它可以接收其他類型的值(包括自定義類型)。
整數(shù)和浮點數(shù)轉(zhuǎn)換
整數(shù)和浮點數(shù)的轉(zhuǎn)換必須顯式指定類型:
let three = 3let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine// pi 等于 3.14159,所以被推測為 Double 類型
這個例子中,常量three的值被用來創(chuàng)建一個Double類型的值,所以加號兩邊的數(shù)類型須相同。如果不進(jìn)行轉(zhuǎn)換,兩者無法相加。浮點數(shù)到整數(shù)的反向轉(zhuǎn)換同樣行,整數(shù)類型可以用Double或者Float類型來初始化:
let integerPi = Int(pi)// integerPi 等于 3,所以被推測為 Int 類型
當(dāng)用這種方式來初始化一個新的整數(shù)值時,浮點值會被截斷。也就是說4.75
會變成4,-3.9會變成-3。
注意:結(jié)合數(shù)字類常量和變量不同于結(jié)合數(shù)字類字面量。字面量3可以直接和字面量0.14159相加,因為數(shù)字字面量本身沒有明確的類型。它們的類型只在編譯器需要求值的時候被推測。
字符與字符串
字符
Swift擁有Character類型,通過標(biāo)明一個Character類型并用字符字面量進(jìn)行賦值,可以建立一個獨立的字符常量或變量:
let exclamationMark: Character = "!"
字符串
Swift擁有String類型,String是例如"hello, world","albatross"這樣的有序的Character(字符)類型的值的集合。通過String類型來表示。 一個String
的內(nèi)容可以用變量的方式讀取,它包括一個Character值的集合。
您可以在您的代碼中包含一段預(yù)定義的字符串值作為字符串字面量。字符串字面量是由雙引號 ("") 包裹著的具有固定順序的文本字符集。 字符串字面量可以用于為常量和變量提供初始值:
let someString = "Some string literal value"
注意someString常量通過字符串字面量進(jìn)行初始化,Swift 會推斷該常量為String類型。
初始化空字符串 (Initializing an Empty String)
要創(chuàng)建一個空字符串作為初始值,可以將空的字符串字面量賦值給變量,也可以初始化一個新的String。實例:
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
// 兩個字符串均為空并等價。
您可以通過檢查其Bool類型的isEmpty屬性來判斷該字符串是否為空:
if emptyString.isEmpty {
print("Nothing to see here")
}// 打印輸出:"Nothing to see here"
字符串可變性 (String Mutability)
您可以通過將一個特定字符串分配給一個變量(用var)來對其進(jìn)行修改,或者分配給一個常量(用let)來保證其不會被修改:
var variableString = "Horse"
variableString += " and carriage"http:// variableString 現(xiàn)在為 "Horse and carriage"
let constantString = "Highlander"constantString += " and another Highlander"http:// 這會報告一個編譯錯誤 (compile-time error) - 常量字符串不可以被修改。
字符串是值類型(Strings Are Value Types)
Swift 的String類型是值類型。 如果您創(chuàng)建了一個新的字符串,那么當(dāng)其進(jìn)行常量、變量賦值操作,或在函數(shù)/方法中傳遞時,會進(jìn)行值拷貝。 任何情況下,都會對已有字符串值創(chuàng)建新副本,并對該新副本進(jìn)行傳遞或賦值操作。
Swift 默認(rèn)字符串拷貝的方式保證了在函數(shù)/方法中傳遞的是字符串的值。 很明顯無論該值來自于哪里,都是您獨自擁有的。 您可以確信傳遞的字符串不會被修改,除非你自己去修改它。
在實際編譯時,Swift 編譯器會優(yōu)化字符串的使用,使實際的復(fù)制只發(fā)生在絕對必要的情況下,這意味著您將字符串作為值類型的同時可以獲得極高的性能。
您可通過for-in循環(huán)來遍歷字符串中的characters屬性來獲取每一個字符的值:
for character in "Dog!".characters {
print(character)
}// D// o// g// !//
字符串可以通過傳遞一個值類型為Character的數(shù)組作為自變量來初始化:
let catCharacters: [Character] = ["C", "a", "t", "!", ""]
let catString = String(catCharacters)
print(catString) // 打印輸出:"Cat!"
連接字符串和字符 (Concatenating Strings and Characters)
字符串可以通過加法運算符(+)相加在一起(或稱“連接”)創(chuàng)建一個新的字符串:
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2// welcome 現(xiàn)在等于 "hello there"
您也可以通過加法賦值運算符 (+=) 將一個字符串添加到一個已經(jīng)存在字符串變量上:
var instruction = "look over"
instruction += string2// instruction 現(xiàn)在等于 "look over there"
您可以用append()方法將一個字符附加到一個字符串變量的尾部:
let exclamationMark: Character = "!"
welcome.append(exclamationMark)// welcome 現(xiàn)在等于 "hello there!"
這些方法是不是看著很眼熟?嗯,Swift吸收了大量現(xiàn)代腳本語言的做法。注意: 您不能將一個字符串或者字符添加到一個已經(jīng)存在的字符變量上,因為字符變量只能包含一個字符。
字符串插值 (String Interpolation)
字符串插值是一種在字符串中插入程序表達(dá)式的方式,比起C語言的%輸出法,是一種非常贊的輸出結(jié)果的形式!可以在其中包含常量、變量、字面量和表達(dá)式。 您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
在上面的例子中,multiplier作為(multiplier)被插入到一個字符串常量量中。 當(dāng)創(chuàng)建字符串執(zhí)行插值計算時此占位符會被替換為multiplier實際的值。
multiplier的值也作為字符串中后面表達(dá)式的一部分。 該表達(dá)式計算Double(multiplier) * 2.5的值并將結(jié)果 (7.5) 插入到字符串中。 在這個例子中,表達(dá)式寫為(Double(multiplier) * 2.5)并包含在字符串字面量中。
注意:
插值字符串中寫在括號中的表達(dá)式不能包含非轉(zhuǎn)義反斜杠 (),并且不能包含回車或換行符。不過,插值字符串可以包含其他字面量。
字符串的操作
當(dāng)然,Swift擁有大量字符串操作方法。包括插入和刪除、比較、索引、前后綴、計數(shù)等。
Unicode支持
作為最現(xiàn)代化的語言,Swift 的String和Character類型是完全兼容 Unicode 標(biāo)準(zhǔn)的,對Unicode的支持是所有語言中最完善的。
Unicode 標(biāo)量(Unicode Scalars)
Swift 的String類型是基于 Unicode 標(biāo)量 建立的。 Unicode 標(biāo)量是對應(yīng)字符或者修飾符的唯一的21位數(shù)字,例如U+0061表示小寫的拉丁字母(LATIN SMALL LETTER A)("a"),U+1F425表示小雞表情(FRONT-FACING BABY CHICK
) ("")。
字符串字面量的特殊字符 (Special Characters in String Literals)
字符串字面量可以包含以下特殊字符:
轉(zhuǎn)義字符\0
(空字符)、\
(反斜線)、\t
(水平制表符)、\n
(換行符)、\r
(回車符)、"
(雙引號)、'
(單引號)。
Unicode 標(biāo)量,寫成\u{n}(u為小寫),其中n為任意一到八位十六進(jìn)制數(shù)且可用的 Unicode 位碼。
下面的代碼為各種特殊字符的使用示例。 wiseWords常量包含了兩個雙引號。 dollarSign、blackHeart和sparklingHeart常量演示了三種不同格式的 Unicode 標(biāo)量:
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}" // $, Unicode 標(biāo)量 U+0024
let blackHeart = "\u{2665}" // , Unicode 標(biāo)量 U+2665
let sparklingHeart = "\u{1F496}" // , Unicode 標(biāo)量 U+1F496
可擴(kuò)展的字形群集(Extended Grapheme Clusters)
每一個 Swift 的Character類型代表一個可擴(kuò)展的字形群。 一個可擴(kuò)展的字形群是一個或多個可生成人類可讀的字符 Unicode 標(biāo)量的有序排列。 舉個例子,字母é可以用單一的 Unicode 標(biāo)量é(LATIN SMALL LETTER E WITH ACUTE, 或者U+00E9)來表示。然而一個標(biāo)準(zhǔn)的字母e(LATIN SMALL LETTER E或者U+0065) 加上一個急促重音(COMBINING ACTUE ACCENT)的標(biāo)量(U+0301),這樣一對標(biāo)量就表示了同樣的字母é。 這個急促重音的標(biāo)量形象的將e轉(zhuǎn)換成了é。
在這兩種情況中,字母é代表了一個單一的 Swift 的Character值,同時代表了一個可擴(kuò)展的字形群。 在第一種情況,這個字形群包含一個單一標(biāo)量;而在第二種情況,它是包含兩個標(biāo)量的字形群:
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ?
// eAcute 是 é, combinedEAcute 是 e?
可擴(kuò)展的字符群集是一個靈活的方法,用許多復(fù)雜的腳本字符表示單一的Character值。
字符串的 Unicode 表示形式(Unicode Representations of Strings)
當(dāng)一個 Unicode 字符串被寫進(jìn)文本文件或者其他儲存時,字符串中的 Unicode 標(biāo)量會用 Unicode 定義的幾種編碼格式(encoding forms)編碼。每一個字符串中的小塊編碼都被稱代碼單元(code units)。這些包括 UTF-8 編碼格式(編碼字符串為8位的代碼單元), UTF-16 編碼格式(編碼字符串位16位的代碼單元),以及 UTF-32 編碼格式(編碼字符串32位的代碼單元)。
Swift 提供了幾種不同的方式來訪問字符串的 Unicode 表示形式。 您可以利用for-in來對字符串進(jìn)行遍歷,從而以 Unicode 可擴(kuò)展的字符群集的方式訪問每一個Character值。
另外,能夠以其他三種 Unicode 兼容的方式訪問字符串的值:
UTF-8 代碼單元集合 (利用字符串的utf8屬性進(jìn)行訪問)
UTF-16 代碼單元集合 (利用字符串的utf16屬性進(jìn)行訪問)
21位的 Unicode 標(biāo)量值集合,也就是字符串的 UTF-32 編碼格式 (利用字符串的unicodeScalars屬性進(jìn)行訪問)
下面由D,o,g,(DOUBLE EXCLAMATION MARK, Unicode 標(biāo)量 U+203C)和
(DOG FACE,Unicode 標(biāo)量為U+1F436)組成的字符串中的每一個字符代表著一種不同的表示:
let dogString = "Dog"
//UTF-8表示
for codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 226 128 188 240 159 144 182
//UTF-16表示
for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 8252 55357 56374
for scalar in dogString.unicodeScalars {
print("\(scalar.value) ", terminator: "")
}
print("")
// 68 111 103 8252 128054
結(jié)論
結(jié)論:無論從表達(dá)能力、方便程度、安全度衡量,Swift在基本數(shù)據(jù)結(jié)構(gòu)方面都是最完善的。