第六部分
- #define、#undef
- 条件编译 #ifdef、#endif、#else、#ifndef
#define、#undef
预处理程序语句使用井号(#)标记,这个符号(#)必须是一行中的第一个非空格字符,但是#号后面可以允许多个空格。
#define通常放在程序的开始,但在#import或include之后。但这不是必须的,它可以放在程序的任何地方。但在使用它之前,它必须已经定义了。
如果使用#define定义了一个宏,可以使用#undef取消定义。
#define IPAD 1
//取消定义
#undef IPAD
条件编译 #ifdef、#endif、#else、#ifndef
#ifdef、#endif、#else、#ifndef
#ifndef:如果没有定义...
- 使用条件编译定义一些参数,可以让程序在不同系统上编译运行。因为不同系统可能有时候参数会不同。
例子,通过判断是否定义了IPAD来定义不同的kImageFile:
#ifdef IPAD
# define kImageFile @"barnHD.png"
#else
# define kImageFile @"barn.png"
#endif
#define IPAD 1
或者
#define IPAD 就足够满足定义了IPAD了
- 还有其他一些作用,比如调试程序。
这里没看懂???
#ifdef DEBUG
NSLog(@"ssss");
#endif
#if、#else、#elif(相当于else if)
- 这几个就相当于if else 语句,只不过是预处理语句形式的条件编译。语法使用和if-else语句相同
#if MAC_OC < 10.5
#define MAC xxx
#endif
- 特殊运算符:defined (name)
#if defined(IPAD) 等于 #ifdef IPAD
#if !defined(IPAD) 等于 #ifndef IPAD
第七部分
- 数组
- 块(Blocks)
- 结构体
- 结构体指针
- 箭头运算符(->)
数组
1. 数组的声明
数组的声明涉及数组所包含元素的数值类型,如int、float或者对象,以及将存储在数组中的最大元素数目。也就是说,对象类型也可以作为数组的元素类型。
int array[100];
float array1[2][3];
NSString *strArray[100];//NSString *作为数组的元素类型
NSString *str[100] = {@"sdf",@"sdfs"};
2. 数组的初始化
- 正如在声明数组的时候可以给它赋初值一样,也可以在声明数组的时候给数组元素赋初值。
int array[4] = {0,1,2,3};
char charArray[5] = {'a','b','c','d','e'};
- 不必完全初始化整个数组,可以指定少数值,然后数组剩余的会自动被设为0。
float array[100] = {100.1,20.4,2003.1};
- 可以不显式地指定数组的大小,但是必须初始化数组的每个元素。
int array[] = {0,1,2,3,4,5,6};
但是不能下面这样初始化,这样初始化是错误的。不指定数组个数时,必须同时初始化。
int array[];
array = {0,1,2,3,4,5,6};
3. 二维数组的初始化
- 第一种,使用花括号和逗号隔开每行的元素。注意花括号和逗号的使用。
int array2[3][4] = {
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}
};
- 第二种,不使用花括号。
int array2[3][4] = {1,2,3,4,1,2,3,4,1,2,3,4};
- 第三种,与一维数组相同,不必完全初始化所有元素。
注意:代码中两种初始化的数组元素是不同的(使用花括号,初始化的是每一行)
int array2[3][4] = {
{1,2},
{1,2,3},
{1,2}
};
int array2[3][4] = {1,2,3,4,1};
块(Blocks)
块是Apple公司对C语言的一种扩展。
- 可以给块传递参数,正如给函数传递一样;
- 块也具有返回值,正如函数的返回值一样;
- 与函数不同的是,块定义在函数或方法的内部,并且能够访问在++函数或者方法范围内++块之外的所有变量。就是说,块可以访问外部变量,但是不能超过块所在的函数或方法的范围。一般来讲,这些变量可以在块内部被访问,但是不能再块内部被修改。除非使用块修改器。
//无返回值,无参数
void (^printMessage)(void) = ^(void){
NSLog(@"xxxxx");
};
//有返回值,两个参数
int (^calculate) (int,int) = ^(int x, int y){
return x+y;
};
结构体
结构体是另一种组合元素的工具。
结构体的定义、声明和初始化
- 结构体的定义
//正常定义
struct date{
int month;
int day;
int year;
};
//结合typedef的定义
typedef struct date{
int month;
int day;
int year;
}Date;
- 结构体的变量声明
//正常声明
struct date today;
struct date tomorrow;
//typedef了别名的声明
Date yesterday;
//在定义结构体的时候,直接声明变量
struct date{
int month;
int day;
int year;
}everyday;
注意:上面的today、tomorrow、everyday是一个结构体变量,而不是一个对象。
3.结构体变量的初始化
- 初始化方法和数组相同。将结构体的元素放到一对花括号内。
struct date today = {1,2,3};
Date tomorrow = {1,2,3};
- 在定义结构体的时候,直接声明并初始化变量
struct date{
int month;
int day;
int year;
}everyday = {1,2,3};
- 使用点语法初始化结构体变量。是的,结构体变量可以使用点语法,这点跟操作对象的属性一样(它的本质是存取方法)。编译器在遇到点运算符的时候,会判断左边是一个对象还是一个结构体。结构体的点语法也有存值和取值的操作。
today.month = 1;
today.day = 2;
today.year = 3;
- 可以使用点语法,任意顺序初始化结构体变量。
struct date today = {.month=1,.today=1,.year=1};
//只将year设为1
Date tomorrow = {.year=1};
结构体中的结构体
CGPoint、CGSize、CGRect都是结构体。
- CGPoint用于描述(x,y)点。
在Apple的CGGeometry.h的头文件中的定义:
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
注意:CGFloat是由typedef定义的基本浮点数据类型。
- CGSize用于描述宽和高。
在Apple的CGGeometry.h的头文件中的定义:
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CGSize CGSize;
- CGRect用于描述包含一个原点(一个CGPoint)和尺寸(一个CGSize)的矩形。
在Apple的CGGeometry.h的头文件中的定义:
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
4.它们的初始化
CGRect rect = CGRectMake(0, 0, 5, 34);
CGSize size;
size.width = 1;
size.height = 1;
CGPoint point = CGPointMake(10, 11);
结构体数组
struct date{
int month;
int day;
int year;
}everyday[100];
struct date today[11];
结构体指针
在使用结构体定义链表或者是树的时候,我们需要指针来操作链表或树的节点,所以在声明一个链表或树的时候,我们用指针来声明。
- 链表的结构体定义
typedef struct ListNode{
int value;
struct ListNode *next;
}ListNode;
- 链表的结构体声明。(定义的结构体指针只是说明这个指针是指向这个结构的,但并未给结构体分配内存空间,这就类似于声明,只是告诉系统我这个指针是干什么用的。所以需要使用malloc)
ListNode *head;
head = (ListNode *)malloc(sizeof(ListNode));//分配内存
head->value = 1;
head->next = nil;
注意,这里使用的是箭头操作符(左边的操作数是指针时,才能使用箭头操作符。->指向的是,指针所指向的对象的成员。)
其实上面的声明和这个是相同的:
ListNode head = {1,nil};
ListNode *pHead = &head;(head取地址)
(*pHead).value = 1;
注意:点运算符比取内容运算符高*,所以这里要加上括号。
(pHead).value 就相当于 head->value*
箭头运算符(->)
C语言:存取结构体成员的点运算符(.)和箭头运算符(->)的区别
- 点操作符和箭头操作符的异同点
相同点:两个都是二元操作符,其右操作符是成员的名称。
不同点:点操作符左边的操作数是一个“结果为结构”的表达式;箭头操作符左边的操作数是一个指向结构的指针。
也就是说,箭头操作符的左边必须是一个指针。箭头操作符指向的是,指针所指向的对象的成员。
因为点操作符不能使用于指针,所以在链表的结构体中,使用箭头操作符。具体看结构体指针的使用。
第八部分
- C语言中的字符串
- 指针
C语言中的字符串
在C语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
下面的声明和初始化创建了一个 "Hello" 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
其实,您不需要把 null 字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 '\0' 放在字符串的末尾。
依据数组初始化规则,您可以把上面的语句写成以下语句:
char greeting[] = "Hello";
但以下声明是错误的:
char greeting[];
greeting = "Hello";
但是以下声明是错误的:
char greeting[];
greeting = "Hello";
指针
指针实际上就是内存地址。
- 可以将指针作为参数传递给方法或函数,也可以让函数或方法返回指针;
- alloc和init返回的就是指针;
例子:使用方法交换两个数字,这里传的就是指针
//交换两个数字
void exchange(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int main (int argc,char* argv[]){
@autoreleasepool{
int a = 1;
int b = 2;
exchange(&a,&b);
}
}
指针与数组
oc编译器将没有下标的数组名看作是指向数组第一个元素的指针。
- 在下面的例子里:首先定义了一个int类型的指针,然后将数组的第一个元素的指针和第二个元素的指针赋给它。*value就等于value[0],*(value+i)就等于value[i]。
int value[100];
int *valuePtr;//int类型的指针
valuePtr = value;
*valuePtr = 1;
valuePtr = value+1;
*valuePtr = 2;
NSLog(@"%d,%d",value[0],value[1]);//输出1,2
- 利用指针求一个数组的和
int arraySum(int array[],int n){
int sum = 0 ,*ptr;
int *arrayEnd = array + n;//注意,这里是数组的最后一个元素“之后”的指针
for ( ptr = array ; ptr < arrayEnd ; ptr++ ) {
sum += *ptr;
}
return sum;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int number[10] = {1,2,3,4,5,6,7,8,9,10};
int result = arraySum(number,10);
NSLog(@"%d",result);
}
return 0;
}
在这里,我们发现,给函数传递数组作为参数的时候,传递的是数组名称(实际上,传递的就是数组的首元素指针)。
字符串指针
- 利用char数组复制字符串。字符数组可以用字符串赋值,char from[] = "hello,world";换句话说,字符数组就是C风格的字符串。
void copyString(char to[],char from[]){
int i;
for ( i = 0 ; from[i] != '\0' ; i++ ) {
to[i] = from[i];
}
to[i] = '\0';
NSLog(@"%s",to);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
char from[] = "hello,world";
char to[50];
copyString(to, from);
}
return 0;
}
- 利用指针复制字符串。注意,这里传的是指针,不是数组。
void copyString(char *to,char *from){
for ( ; *from != '\0' ; to++,from++ ) {
*to = *from;
}
*to = '\0';
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
char from[] = "hello,world";
char to[50];
copyString(to, from);
NSLog(@"%s",to);
copyString(to, "hello,world");
NSLog(@"%s",to);
}
return 0;
}
注意:
- "hello,world"是C风格的字符串,不是字符串对象,字符串对象前面有@符号,这两种类型不能互相转换。
- 字符数组 char a[] = "ssdaf dfa"; 字符数组就是C风格的字符串。
- 如果一个函数需要传入一个char类型的数组,你可以传入一个字符数组,或者是一个字符串,而不能是字符串对象。
- 上面的copyString(to, "hello,world"); 表明,在传递字符串的时候,实际上传递的也是指向字符串的指针。
可以这样写一个字符指针。在oc中,只要用到字符串(不是字符串对象),就会产生指向该字符串的指针。
char *textPtr = "hello,world";
第九部分
- sizeof运算符
- 库函数malloc
- 一些工作原理
sizeof运算符
sizeof运算符用来确定数据类型或者是对象的大小;
sizeof返回的是字节大小;
sizeof运算符的参数可以是变量、数组名称、基本数据类型名称、对象、派生数据类型名称、表达式。
获取对象的大小,要带上星号,否则返回的是指针大小:sizeof(对象)*
假设*myFract是一个Fraction的对象,它包含两个int实例变量。那么,表达式sizeof(myFract)返回的是4,在任何使用4个字节表示指针的系统上都会返回4,这是因为sizeof(myFract)表示的是指针大小。要获得实际存储Fraction对象实例的数据结构大小,要这样写:
sizeof(*myFract)
malloc
malloc用来实现动态分配内存。
一些工作原理
1. 实例变量实际上存储在结构中
定义一个新类和它的实例变量时,这些实例变量实际上存放在一个结构中。这就说明了可以如何处理对象,对象实际上是结构,结构中的成员是实例变量。所以继承的实例变量加上你再类中添加的变量就组成了一个结构。使用alloc分配对象时,系统预留了足够的空间来存储这些结构。
结构中有一个从根对象继承的成员,名为isa,它用来确定对象所属的类,它是结构的一部分,所以由对象携带。这样,运行时系统只需通过查看isa成员,就可以确定对象所属的类。
2. 对象变量实际上是指针
Fraction *myFract;
事实上是定义了一个名为myFract的指针变量。这个变量定义为指向Fraction类型的数据。
myFract = [Fraction alloc];
上面是在为Fraction对象的新势力分配内存(即存放结构的空间),然后使用结构的指针,并将指针变量myFract存储在其中。
3. id类型是通用指针类型
- 因为通过指针(也就是内存地址)来引用对象,所以可以自由地将它们在id变量之间来回赋值。
- 返回id类型值的方法,只是返回的某对象的指针。
- 因为对象携带者isa成员,所以系统总是能确定id类型变量所属的类。
完