Struct結構體
node
c沒有面向對象編程的概念。因此爲了建立一個複雜的數據結構(不是基本數據類型和數組),你必須使用結構體。在某些Objective-C代碼中,你可能甚至常常看到結構體被使用,這樣作是爲了節省內存。算法
例如,CGPoint,CGRect,和CGSize都是結構體。蘋果開發者把他們做爲結構體是由於在iPhone中構建views的時候要頻繁的使用到。你能夠在Objective-C中使用結構體。編程
struct point { 數組
int x;
數據結構
int y;app
};ide
struct point add_point(struct point p1, struct point p2) {函數
p1.x += p2.x;
p1.y += p2.y;
return p1;oop
} 性能
若是你要傳遞一個大的結構體數據給函數,你應該考慮傳遞結構體指針,這樣可以避免拷貝整個結構體(由於是值傳遞)。在其餘一般狀況下你會看到結構體指針。
struct point origin;
struct point *porigin;
porigin = &origin;
printf("origin is (%d, %d) \n" , (*porigin).x , (*porigin ).y );
動態內存分配
和Objective-C比較,c中的內存管理有一些相同點和不一樣點。在c中,你能夠create和allocate內存給對象;若是你爲一個對象分配了內存,你不要手動的deallocate/free這個對象,釋放內存。在Objective-C的autorelease或Autorelease Pool中,沒有這樣的概念。
爲了對c中的內存管理有一個很好的理解,你必須記住4個重要的函數,如表格9-1
malloc:你能夠申請一個指定大小的內存快,而後返回一個void類型的指針。你能夠把這個指針轉換成你指定的類型,如:
my_ptr = (cast_type *)malloc(number_of_bytes); // General format
my_ptr = (int *)malloc (100 * sizeof(int)); // allocate a pointer of integer with size
//of 100 integer
calloc:它一般用來申請多個內存塊,每一個塊的大小相同,而後把他們全部字節都設置爲0
my_ptr = (cast_type *)calloc(number_of_elements, size_of_element); // General format
my_ptr = (int *)calloc (100, sizeof(int)); // allocate a pointer of integer with size
// of 100 integer
free:這個內存管理機制相似於Objective-C:你分配的內存,你須要釋放它。你能夠在c中使用free進行釋放。
free(my_ptr);
realloc:有時候你爲對象或數組分配的內存不夠,所以你須要改變內存的大小,經過realloc能夠用實現。
realloc(my_ptr, 200 * sizeof(int));
注意:你不能再一次使用函數 malloc/calloc,由於將會擦除你指針指向的內存中存儲的數據。 |
Linked List 例子
是時候從理論中走出來,而後開始寫代碼了。你將用你學到的知識來解決用鏈表存儲數據的問題。已經在第5章學習了用Ogjective--C來實現。如今,你將學會如何用c寫一個鏈表,在不少狀況下,會有更高的性能。
#include <stdio.h>
#include <stdlib.h>
struct Node {
int number;
struct Node *next;
};
爲了獲得一個鏈表,你須要一個結構體引用自身。節點結構體內部須要有一個link來指向下一個結構體節點。在c中,你能夠在頭文件或實現文件中聲明方法接口。
void append_node(struct Node *list, int num);
void display_list(struct Node *list);
int main(void) {
struct Node *list;
list = (struct Node *)malloc(sizeof(struct Node));list->number = 0;
list->next = NULL;
append_node(list, 1);
append_node(list, 5);append_node(list, 3);
display_list(list);
// delete a list here. free(list) will not work
return(0);
}
void delete_list(struct Node *list) {
// do it as your exercise
}
void display_list(struct Node *list) {
// loop over the list to print the value out.while(list->next != NULL) {
printf("%d ", list->number);
list = list->next;}
printf("%d", list->number);
}
void append_node(struct Node *list, int num) {
// go into the end of the list
while(list->next != NULL)
list = list->next;
// set the next property for the last Node object.
list->next = (struct Node *)malloc(sizeof(struct Node));
list->next->number = num;
list->next->next = NULL;
}
你能夠看到,由於你的鏈表沒有固定大小,你老是要使用malloc來分配新的元素。在用完鏈表以後,你要刪除它。你須要記住內存管理規則:對於每次調用malloc或calloc,你須要調用free函數;不然,會有內存泄露。像在代碼中的警告註釋,簡單的調用free(list)會致使一些內存泄露。我將delete_list的實現做爲一個練習留給你來作。
函數指針
在c中,函數不是一個變量,可是你能夠定義一個指針指向它,就像定義指針指向一個整數或結構體。你能夠把這些指針放到一個數組中,而後把這些函數指針做爲參數傳遞給其餘函數。這個和Objective-C中的selector是相似的。
接下來你會看到一個簡單的例子,在快速排序算法中實現了一個比較方法。qsort是c中的一個內置函數,可以獲得一個函數指針,而後使用快速排序算法對數組進行排序。
這是qsort的接口:
void qsort (void *array, int number_of_elements, int size_of_element, int (* comparator)
(const void *, const void *) );
你須要一個比較函數,它接收兩個指針,返回一個整數表示什麼值更大。
int compare (const void * a, const void * b) {
return ( *(int*)a - *(int*)b );
}
而後你能夠把它做爲參數傳遞給qsort函數。
int main () {
int values[] = { 40, 10, 100, 90, 20, 25 };
qsort (values, 6, sizeof(int), compare); // pass in the compare function here.return 0;
}
Bitwise Operators位操做符
位操做運算比加法和減法稍快一些,比乘法和除法快不少。你也許會在一些庫中看到使用位操做符,尤爲是用比較老的微處理器寫的。
瞭解位操做符可以幫助你操做位,在密集型計算中獲得更好的性能。只有幾個位操做符合移位操做符須要記住:NOT, AND, OR, XOR, left shfit,和right shift。
NOT 邏輯非,0變1,1變0.
NOT 0111 = 1 000
AND 邏輯與,只要有一個爲0,結果就是0;不然爲1
0 1 0 1
0 0 1 1
AND
0 0 0 1
OR 邏輯或,只要有一個爲1,結果就是1;不然爲 0
0 0 1 0
1 0 0 0
OR
1 0 1 0
XOR 邏輯異或,相同爲0,不一樣爲1
0 1 0 1
0 0 1 1
XOR
0 1 1 0
使用這些位操做符,你可以很是快的改變bit值。若是你不常用位操做的話,這看起來會很是的奇怪。我會在Objective-c中給你一個說明。這裏有一個使用了位操做的Cocoa Touch Framework中的應用,假設你已經對NSCalendar很是熟悉了。
在一個NSCalendar中,你能夠基於一個輸入參數獲得一個指定日期的日期組件列表。例如,若是你想要一個NSDataComponent對象,它包含了你想要的日期組件,你可使用下面的源代碼:
NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *dateComponents = [calendar components:unitFlags
fromDate:startDatetoDate:endDate
options:0];
當方法[calendar components:fromDate:toDate:options] 使用unitFlag被調用時,會檢查調用者須要什麼樣的日期組件,而後會返回一個確切的組件類型。這樣讀寫代碼都是很是方便的,只須要使用OR操做符。
在方法[calendar components:fromDate:toDate:options] 中,會使用 AND 操做符檢查unitFlag的值。
BOOL hasYear = (unitFlags & NSYearCalendarUnit) != 0;
BOOL hasMonth = (unitFlags & NSMonthCalendarUnit) != 0;
BOOL hasWeek = (unitFlags & NSWeeCalendarUnit) != 0;
...
如今,我詳細解釋一下內部發生了什麼以及是如何工做的。首先,每一個flag(NSYearCalendarUnit, NSMonthCalendarUnit,等等 )被賦予了一個惟一的二進制值,用這樣的格式來表示:0100,1000。
當你在這三個flags上進行OR操做時,你會獲得相似1011這樣的值。你把這個值傳遞給方法
[calendar components:fromDate:toDate:options] 。在這個方法的內部,它會將存儲在內部的每個flag作AND操做。
1011 & 0100 = 0100there is a NSYearCalendarUnit.
1011 & 1000 = 1000 there is a NSMonthCalendarUnit
相比其餘普通的方法(要麼傳遞不少參數,要麼使用一個很大的枚舉,或者須要一個很大的循環來檢查數據),使用這種方法將會提高你應用程序的性能。
位的移動:當你要乘或除一個2的倍數時,你能夠對位進行左移或右移。例如,若是你要乘或除2,4,6,8,16......,你能夠考慮使用位的移動,比直接使用乘法或除法性能高不少。你只能在整數上
有兩個移位操做符:左移和右移。整數是以二進制的形式存儲的,例如0000 0110(十進制是6)。
左移:把左邊的位移出去,右邊用0補充。若是你左移了n位,至關於乘了2的n次方。
右移:把右邊的位移出去,左邊用c補充。若是你右移了n位,至關於除了2的n次方。
0000 0110 >> 1 = 0000 0011(十進制3)