1.关键字:typedef
用法一:typedef char * pstr;相当于把char *重命名为pstr; pstr==char *
用法二:
typedef struct Book{
char name[20];
float price;
}BOOK; //相当于重新把结构体命名为BOOK,在主函数中可以直接定义结构体变量 BOOK book1,book2....;
用法三:
typedef unsigned long size_t;
typedef unsigned int size_t;
用法四: 复杂类型;
2.static:用法
1.修饰在函数内定义的变量(初始化后),变量的作用域在当前函数内有效,变量在内存中的.data段直到整个源程序结束释放;
2.修饰在函数外的变量,作用域从定义的位置开始到当前模块结束;
3.static 修饰函数,表示该函数只能被该模块内的函数使用,对其他模块不能使用;
3.内存空间
1.申请内存空间;
2.给申请的这块空间赋初值;( memset() )
3.判断内存空间是否申请成功;
4.使用这块内存空间;
5.释放申请的内存空间;
6.给指针赋值为空;(防止野指针的出现)
extern:告诉编译器,这个变量是外来的变量;
extern int number;//声明变量;
4.题目
//交换两个数的值;
/*#include <stdio.h>
void swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
int main(int argc,const char *argv[])
{
int m,n;
scanf("%d%d",&m,&n);
swap(&m,&n);
printf("%d %d",m,n);
return 0;
}*/
//空格作为分隔符统计输入单词个数;面试常考题;
//int main(int argc,const char *argv[])
//{
// char str[200];
// scanf("%[^\n]",str);
// int i=0;
// int flag=1;
// int cnt=0;
// while (str[i]!='\0') {
// if (str[i]==' ') {
// if (!flag) {
// flag=1;
// }
//
// }
// else
// {
// if (flag) {
// cnt++;
// flag=0;
// }
// }
// i++;
//
// }
// printf("%d\n",cnt);
// return 0;
//
//}
辗转相除法求最大公约数
/*#include <stdio.h>
intmain(int argc,const char *argv[])
{
inta,b;
scanf("%d%d",&a,&b);
while (a%b) {
inttmp;
tmp=a%b;
a=b;
b=tmp;
}
printf("%d\n",b);
return 0;
}*/
//三目运算符: ? :
//表达式1? 表达式2: 表达式3
//表达式1的值为真,则整个表达式的值为表达式2的值
//表达式1的值为假,则整个表达式的值为表达式3的值
/*int main(int argc, const char *argv[])
{
int a , b, c;
a= 10;
b= 20;
c= 30;
printf("%d\n", a<b? a: b);
printf("%d\n", a>b? a : b>c? b:c);//右结合性
printf("%d\n", a>b? a :(b>c?b :c));
}*/
5.内存分配方式
内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
6.常见的内存错误及其对策
发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。
常见的内存错误及其对策如下:
内存分配未成功,却使用了它。
编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。
内存分配虽然成功,但是尚未初始化就引用它。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数jj组)。
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
忘记了释放内存,造成内存泄露。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。
释放了内存却继续使用它。
有三种情况:
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
(3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
【规则7-2-1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
【规则7-2-2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
【规则7-2-3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
【规则7-2-4】动态内存的申请与释放必须配对,防止内存泄漏。
【规则7-2-5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。*/
7.内存存储
//内存从下往上.text)
/*
1.代码段:代码段(codesegment/text segment)通常是指用来存放程序执行代码的一块内存区域。
2.数据段:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配,可以分为只读数据段和读写数据段。字符串常量等,但一般都是放在只读数据段中。
3.BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量(static)
4.堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。向上递增
5.栈 (stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
*/
8.static关键字
//1.在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。\
//2.在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
//3.在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
//(1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
//(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
//(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
9.队列分两种
//1、串行队列:凡是向串行队列上分配的任务、任务会按分配的现后顺序串行执行。
//2、并行队列:凡是向并行队列上异步分配的任务、一般情况下任务会在各自不同的线程中并发执行、如果是同步分配、会有相应的优化措施。
//创建一个串行队列
//dispatch_queue_t myqueue =dispatch_queue_create("com.qianfeng.xxx", DISPATCH_QUEUE_SERIAL);
//创建一个并行队列
//dispatch_queue_t myqueue =dispatch_queue_create("com.qianfeng.xxx", DISPATCH_QUEUE_CONCURRENT);
//有两个系统已经提前创建好的队列:
//1、全局队列(它是一个并行队列) dispatch_get_global_queue
//2、主队列(它是一个串行队列)、主线程中任务是被分配到主队列上面的 dispatch_get_main_queue()
//串行:一条一条的执行;
//并行:一起执行,先执行后执行不确定;
//异步: 会立即返回,开辟一个新线程,把任务分配到新线程中,立即返回,任务在新线程中执行不需要立即得出结果.类似后台下载一样; dispatch_async
//同步: 不立即返回,不会开辟新线程,还在这个线程中一步一步的执行,一直到任务执行完毕,得出结果了,才会执行后面的任务; dispatch_sync
10.题目示范:
1.when to use NSMutableArray and when touse NSArray?
1当数组元素需要动态地添加或者删除是,用NSMutableArray
2当数组元素固定不变时用NSArray.
2.Give us example of what are delegatemethods and what are data source methods of UITableView.
1代理方法:返回tableView每行的高度,监听tableview每行的选中
2数据源方法:返回tableview数据的组数和行数,每行显示什么数据;
3.Different between shallow copy and deepcopy?
1浅拷贝:指针(地址)拷贝,不会产生新对象
2深拷贝:内容拷贝,会产生新对象.
4.what is advantage of categories? What isdifference between implementing a category and inheritance?
1分类可以在不修改原来类模型的基础上拓充方法
2分类只能扩充方法,不能扩充成员变量,继承可以扩充方法和成员变量.
3继承会产生新的类.
5.OC中创建线程的方法是什么? 如何制定在主线程中执行代码?如何延时执行代码?
1创建线程的方法
NSThread NSOperationQueue和NSOperation GCD
2主线程中执行代码
1[self performSelectorOnMainThread:withObject:waitUntilDone:];
2[self performSelector: onThread:[NSThread mainThread] withObject:waitUntilDone:];
3dispatch_async(dispath_get_main_queue(),^{ });
11.@property
@prperty只不过是给编译器看的一种指令,它可以编译之后为你生成相应的getter和setter方法。而且,注意看到面property(nonatomic,copy)括号里面这copy参数了吗?它所做的事就是
_name = [name copy];
首先,我们看atomic 与nonatomic的区别与用法,讲之前,我们先看下面这段代码:
@property(nonatomic, retain) UITextField*userName; //1
@property(nonatomic, retain,readwrite)UITextField *userName; //2
@property(atomic, retain) UITextField*userName; //3
@property(retain) UITextField*userName; //4
@property(atomic,assign) int i; // 5
@property(atomic) int i; //6
@property int i; //7
请读者先停下来想一想,它们有什么区别呢?
上面的代码1和2是等价的,3和4是等价的,5,6,7是等价的。也就是说atomic是默认行为,assign是默认行为,readwrite是默认行为。但是,如果你写上@property(nontomic)NSString *name;那么将会报一个警告,如下图:
因为是非gc的对象,所以默认的assign修饰符是不行的。那么什么时候用assign、什么时候用retain和copy呢?推荐做法是NSString用copy,delegate用assign(且一定要用assign,不要问为什么,只管去用就是了,以后你会明白的),非objc数据类型,比如int,float等基本数据类型用assign(默认就是assign),而其它objc类型,比如NSArray,NSDate用retain。
12.Objective-C如何对内存管理的,说说你的看法和解决方法?
1每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁.
2通过retain可以让对象的计数器+1, release可以让对象的计数器-1;
3还可以通过autoreleasepool 管理内存.
4如果用ARC,编译器会自动生成管理内存的代码.
内存管理的方式有: 手动内存管理,ARC自动引用计数,内存池
13.内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象
需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
谁申请,谁释放
遵循Cocoa Touch的使用原则;
内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。
关键字alloc 或new 生成的对象需要手动释放;
设置正确的property属性,对于retain需要在合适的地方释放,
1只要调用了alloc,copy , new 方法产生了一个新对象,都必须在最后调用一次release或者autorelease
2只要调用了retain,都必须在最后调用一次release或者autorelease.
3@property 如果用了copy或者retain,就需要对不再使用的属性做一次release操作.
4如果用力ARC,另行讨论.