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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。