您好,登錄后才能下訂單哦!
Struct結構體
c沒有面向對象編程的概念。所以為了創建一個復雜的數據結構(不是基本數據類型和數組),你必須使用結構體。在某些Objective-C代碼中,你可能甚至經常看到結構體被使用,這樣做是為了節省內存。
例如,CGPoint,CGRect,和CGSize都是結構體。蘋果開發者把他們作為結構體是因為在iPhone中構建views的時候要頻繁的使用到。你可以在Objective-C中使用結構體。
struct point {
int x;
int y;
};
struct point add_point(struct point p1, struct point p2) {
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
如果你要傳遞一個大的結構體數據給函數,你應該考慮傳遞結構體指針,這樣能夠避免拷貝整個結構體(因為是值傳遞)。在其他通常情況下你會看到結構體指針。
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)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。