- Objective-C和Sprite Kit游戲開發從入門到精通
- 曹化宇
- 4774字
- 2021-01-07 18:57:37
2.5 整數
輕松一刻,做個小游戲,猜一猜下面的代碼會顯示什么結果?
int xPos; xPos = 105.5; NSLog(@"%i", xPos);
105.5嗎?錯了!
你也許會想,這是為什么呢?明明寫的是105.5,怎么就總是顯示105呢?問題就在于把xPos變量聲明為整數(int)了。
原來int類型不能處理小數呀!這下就對了!
好的,是時候考慮數據的類型了。在Objective-C中,數據類型可以分為兩種風格和若干種類型。其中,兩種風格是指:
? C風格的基本類型,如int、unsingned int、long int、float、double、char等,這些類型與C語言中的數據類型應用是相似的。
? 在Foundation框架中也定義了一些數據類型,如NSInteger、NSUInteger、NSString、CGFloat等。實際上,這些數據類型只是C數據類型的別名,在后續的學習中,我們會逐步發現使用這些類型的意義所在。此外,Foundation是在iOS和OS X系統中進行開發的基本框架,在這個框架中,定義了一系列的基本數據類型和開發資源。也許你會想起來,我們的代碼中都引用了Foundation.h頭文件。
接下來是真正的數據類型,包括整數、浮點數、布爾、字符、字符串等,我們會詳細討論各種數據類型的取值范圍、運算、類型轉換等應用特點,首先從整數開始。
■2.5.1 取值范圍
整數是指沒有小數部分的數,按照計算機內部處理方式又分為有符號整數和無符號整數。其中,有符號整數可以處理負數、零和正數,而無符號整數只能處理零和正數。
在C風格數據類型中,int和unsigned int是兩種比較常用的整數類型。其中,int是有符號整數(singn int)的簡寫形式,而unsigned int則是無符號整數。它們都定義為32位整數,這樣一來,int類型的取值范圍就是-2147483648到2147483647,即-231到231-1,相對應,unsigned int的取值范圍則是0到232-1。
在Foundation框架中,常用的整數類型為NSInteger和NSUInteger,如果我們呼叫幫助,就會發現,它們實際使用了typeof關鍵字定義為C數據類型的別名,在64位環境下,它們分別定義為long和unsinged long,而在32位環境下,它們分別定義為int和unsinged int類型。我們知道,iOS和OS X系統都已進入64位時代,所以,在開發較新平臺下的項目時,我們可以認為NSInteger和NSUInteger類型分別是long int和unsigned long int類型的別名。
那么,為什么要使用這些Foundation中定義的數據類型呢?我想,最大的優勢在于代碼對各種平臺的兼容性。
■2.5.2 算術運算
接下來,我們進行基本的算術運算訓練,就是加、減、乘、除那些東西,很簡單,只是記得要使用Objective-C代碼!
第一題:1號機庫有30架戰斗機,2號機庫有29架戰斗機,一共有多少架戰斗機,在程序中應該怎么寫?
答:用加法(+),如下面的代碼。
NSInteger hangar1 = 30; NSInteger hangar2 = 29; NSInteger sum = hangar1 + hangar2; NSLog(@"共有%li架戰斗機", sum);
第二題:要是飛走了16架,還剩多少架,怎么計算呢?
答:用減法(-)呀!我們接著前面的代碼寫。
NSInteger overplus = sum -16; NSLog(@"剩余%li架戰斗機", overplus);
第三題:一個飛行中隊有4架戰斗機,那14個中隊有多少架呢?
等等,我知道,用乘法(*),小學生都會。
NSInteger squadrons = 14; sum = squadrons * 4; NSLog(@"14個中隊共%li架戰斗機", sum);
第四題:那2號機庫里有幾個飛行中隊呢?
答:用除法(/),如下面的代碼。
squadrons = hangar2 / 4; NSLog(@"2號機庫有%li個飛行中隊", squadrons);
在Xcode中測試之前,先想一想結果是什么?
第五題:好像2號機庫里的戰斗機數量不能被4整除,分了7個中隊,還剩幾架戰斗機,這個怎么計算呢?
答:這個在程序中就很簡單了,可以直接使用取余數運算符(%)來計算,如下面的代碼。
NSInteger remainder = hangar2 % 4; NSLog(@"2號機庫分完飛行中隊還剩%li架戰斗機", remainder);
以上示例,我們使用整數進行了一系列的算術運算,對于加法、減法和乘法運算,其結果和我們習慣上的概念相同,但請注意除法和取余數運算,其中,整數除以整數,結果依然是整數,而取余數運算的結果也是整數。
此外,我們還應該注意,在任何編程語言中,運算符都會有一定的優先級規則,即一系列運算符在表達式中運算的先后順序。比如,前面的5個算術運算符在一個算式里時,就應該先計算乘、除、取余數,然后再計算加法和減法。
對于運算符的優先級,如果你不能保證百分之百的記憶正確(可是有幾十個運算符),我們還是建議使用小括號()來強制指定運算順序,這樣,代碼會更安全,可讀性也更強,所以,在工作中,我們不會完全依賴默認的運算符優先級。
■2.5.3 NSLog()函數與格式化輸出
在開發和調試過程中,NSLog()函數可以用來顯示各種信息,其中,非文本的數據需要使用格式化字符。前面的內容中,我們已經多次使用NSLog()函數。
在這里,常用的整數格式化字符包括:
? int類型使用“%i”格式化字符,我們還可以使用“%x”將整數顯示為十六進制,使用“%o”將整數顯示為八進制。
? unsigned int類型,無符號整數,使用“%u”格式化字符。
? long int類型,長整型數據,使用“%li”格式化字符。
? unsigned long int類型,無符號長整型數據,使用“%lu”格式化字符。
? long long int類型,長長整型數據,使用“%lli”格式化字符。
? unsigned long long int類型,無符號長長整型數據,使用“%llu”格式化字符。
這些整數類型都有相應的十六進制和八進制的格式化字符,基本的變化原則就是將列表中給出的格式化字符中的i或u變為x(十六進制)或o(八進制)。
■2.5.4 組合運算符
組合運算符是將基本的算術運算符與賦值運算符合并使用,如下面的代碼。
NSInteger x = 1; x += 2; // 相當于x = x + 2 NSLog(@"%li", x); // 3
在Objective-C中,5種算術運算都有相應的組合運算符,即+=、-+、*=、/=和%=運算符。
■2.5.5 增量與減量運算
在整數操作中,還有一種運算很常用,那就是增量計算,簡單的說,就是變量進行加1的運算。增量運算包括兩種形式,即前增量和后增量。
前增量運算表達式中,變量會先進行加1的計算,然后返回表達式的值,此時,表達式的值就是變量加1的值。最終的結果,表達式和變量的值都是變量原值加1,如下面的代碼。
int i = 1; NSLog(@"%i", ++i); // 2 NSLog(@"%i", i); // 2
后增量運算表達式中,表達式會返回變量原來的值,然后變量再進行加1的計算。最終結果,表達式的值就是變量的原值,變量的值會加1,如下面的代碼。
int i = 1; NSLog(@"%i", i++); // 1 NSLog(@"%i", i); // 2
實際應用中,如果我們只需要使用增量計算后變量的值,則前增量和后增量的區別就無所謂了。但是,如果我們需要使用增量運算表達式的值,就必須非常小心前增量和后增量的區別。
此外,與增量運算相對應的減量運算,同樣包括前減量和后減量運算,工作方式與增量運算相似,只是減量運算執行的是變量減1的操作。
■2.5.6 二進制與位運算
對于初學者,本部分可以作為選讀,這也是本書中為數不多的與計算機軟件基本工作原理相關的內容,了解一下不會有壞處。當然,現在不研究,暫時也不會影響我們繼續往下學習,以后有需要,也可以隨時回來串串門。
了解整數的位運算,我們首先需要清楚整數在計算機中的處理方式,也就是整數的二進制形式。前面,我們說過的32位整數、64位整數,指的就是二進制位的數量。
在二進制計算中,運算數只包括0和1。這個原因很簡單,因為計算機所使用的數字電路就只有兩種狀態,即連通和斷開狀態,二進制才能最簡單地表示數字電路的狀態。
1.無符號整數的二進制形式
無論是8位還是64位,它們對于整數處理的基本原理是一樣的,所以,接下來,我們就使用簡單點的8位二進制形式來討論相關的應用問題。你一定和我一樣,也不想看著64個0或1組成的二進制數發呆,對吧!
二進制數如何轉換為十進制整數呢?我們先來看無符號整數及有符號整數中0和正數的轉換方法。
如果二進制位上全部是0,那么,這個數就是0了;如果二進制位上是1,就使用2n-1計算出這個二進制位對應的十進制整數,其中,n是從右向左數的第幾位(由低位向高位),然后,將所有的數值相加,就得到了這個二進制數所表示的十進制整數。
如圖2-4中的00011001,我們可以得到算式:24+23+20=16+8+1=25,最終,我們計算出二進制數00011001表示的十進制整數就是25。

圖2-4 二進制數據
那么,十進制正整數如何轉換為二進制呢?我們還以25為例,如圖2-5所示的計算方法。

圖2-5 十進制轉二進制計算方法
首先,我們使用25除以2,商是12(寫在下方),余數為1(寫在左邊);然后使用12除2,商是6,余數是0;以此類推,直到商為1時結束計算。然后,我們從下方往上寫出1和0,如圖中的結果就寫成11001,如果位數不夠,比如我們需要8位整數,則在前面(高位)補0,最終得到00011001,這就是十進制整數25的二進制形式了。
2.邏輯位運算
下面,我們就來了解Objective-C中的位運算符。
按位與(AND)運算,使用&運算符,包括兩個運算數,當兩個運算數都是1時,結果為1,否則為0。如圖2-6所示。

圖2-6 按位與運算
按位或(OR)運算,使用|運算符,包括兩個運算數,當兩個運算數有一個為1時,結果為1,只有兩個數都是0時,結果才為0。如圖2-7所示。

圖2-7 按位或運算
按位異或(XOR)運算,使用^運算符,包括兩個運算數,當兩個數一樣時,結果為0,只是兩個數不同時,結果才為1。如圖2-8所示。

圖2-8 按位異或運算
按位取反(NOT)運算,使用!運算符,只需要一個運算數,即1變0,0變1。如圖2-9所示。

圖2-9 按位取反運算
3.補碼
一個二進制數的補碼是指原數按位取反后加1的值。還以00011001為例,其按位取反的結果是11100110,再加1就是11100111。這樣,11100111就是00011001的補碼。
那么,在計算機中使用補碼有什么用呢?我們在有符號整數的處理中就可以看到了。
4.有符號整數的二進制形式
我們知道,有符號整數可以處理負數、零和正數。那么,問題來了,在二進制中如何處理整數的符號呢?
答案是,有符號整數二進制的最高位就是它的符號位,如圖2-10所示。

圖2-10 有符號整數的二進制形式
當有符號整數為零或正數時,符號位為0,也就是說,8位有符號整數的最大取值就是01111111,這就是127。
負數的表示就有點意思了,除了符號位為1,其他位上的數據實際上是負數絕對值的補碼形式。
比如,-128的二進制就是10000000(這可不是-0)。我們先看128的二進制形式,即10000000,請注意,此時,我們沒有考慮符號問題,然后,按位取反就是01111111,再加1得到補碼10000000(和原數是一樣的)。
不過,我們現在處理的是8位有符號整數,所以,只有后7位才是真正的數據,所以,128補碼中的最高位1就直接扔掉了(在計算機中就是這么絕),然后換成另外一個1(其含義是負數),這樣,-128的二進制就是10000000。我們可以看到,雖然看上去差不多,但其實際意義是完全不同的。
好的,大家也看到了,-128是8位有符號整數的最小取值,它有點太特殊了,我們換個數試試吧。
比如-25,先看25的二進制數,我們前面已經算過了,就是00011001,它的補碼呢?我們也算過了,就是11100111。別忘了有符號整數的最高位是符號位,8位有符號整數-25的二進制就是11100111。
接下來,我們再算一個好玩的,就是11100111的后七位是十進制的多少,算式如下:
26+25+22+21+20= 64+32+4+2+1 = 103
103有什么特別的?它正好等于128-25,隱約中有沒有想到點什么呢?
那么,補碼有什么特殊用途呢?在計算機內部可以只進行加法操作,而不需要減法操作(真是多一事不如少一事)。下面,我們來看看25-25在計算機中的二進制處理,我們假設數據類型都是8位有符號整數。
首先,25-25可以處理為25+(-25),好的,25的二進制形式是00011001, -25的二進制是11100111。然后,我們進行相加運算,如圖2-11所示。

圖2-11 有符號整數的加法運算
好的,我們正在處理的是8位有符號整數,所以,最終的結果只有00000000,也就是整數0。
現在,大家應該了解了有符號整數和無符號整數的二進制表現形式,以及取值范圍是怎么確定的吧!如果你感興趣,可以按二進制試著計算一下,加法雖不難,只是在加數有些多時,注意不要加錯了。
5.位移運算
關于二進制,最后討論一下位移運算,在Objective-C中,位移運算包括位左移運算(<<運算符)和位右移運算(>>運算符),對于這兩種位移運算,我們同樣按有符號和無符號整數分別討論。
首先是無符號整數的位移操作,以8位無符號整數64為例,它的二進制表示為01000000,那么它右移2位的操作如圖2-12所示。

圖2-12 無符號整數右移2位
我們可以看到,當一個無符號整數右移操作時,會將低位的數據直接舍棄,然后在高位補零。那么,64右移2位后的二進制就是00010000,我們換算成十進制就是16。實際上,我們可以看到,當一個整數(N)進行右移n位時,就是在進行N÷2n的計算。
反過來,當我們進行整數(N)的左移運算時,高位數據舍棄,在低位補0,實際上,就是在進行N×2n的計算。但請注意,當我們進行整數的位移運算時,應考慮整數的取值范圍。如果左移超出了允許的最高位,也就是超出了整數的取值范圍,同樣會舍棄,這時就會造成整數的溢出。
下面的代碼演示了無符號整數的位移運算:
NSLog(@"%ul", 64 >> 2); // 16 NSLog(@"%ul", 16 << 2); // 64
對于有符號整數的位移運算,會有些不同。如進行8位有符號整數-64的右移運算,首先,-64的二進制表示為11000000,那么,它的右移兩位計算操作就如圖2-13所示。

圖2-13 有符號整數右移2位
我們可以看到,在對有符號整數進行移位操作時,只對數據位進行移動,而符號位保持不變;如圖中的右移操作,會在數據的高位補上與符號位相同的數據,這樣,-64右移2位后的二進制數就是11110000,也就是-16。我們可以看到,同樣是在進行N÷2n的操作。
對于有符號整數的左移操作,同樣是只移動數據位,符號位保持不變,然后,在低位補0,這實際上也是在執行N×2n的操作。
下面的代碼演示了有符號整數的位移運算:
NSLog(@"%ul", -64 >> 2); // -16 NSLog(@"%ul", -16 << 2); // -64