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)