C语言基础

C 语言基础

Object-C 语言是C语言的超集,意思就是我们可以将两种语言写在同一个源代码文件中。Object-C语言结构的核心是建立在C语言之上的。所以在学习语言的高级特性之前,掌握C语言基础是很重要的。

The relationship between Objective-C and C

这个模块为C语言提供一个简洁的概述。我们将讨论关于注释、变量、算数运算符、控制流、简单的数据结构、指针。这些概念是我们讨论Object-C面向对象特性的基础。

Comments 注释

在C语言中有两种方式可以提供注释。行内注释,以双斜线开始,到本行结束。块注释,可以跨越多行,但是必须在 /* 和 */ 之间。
比如:

// This is an inline comment

/* This is a block comment.
   It can span multiple lines. */

因为编译器会忽略注释,这样就可以让我们在代码的旁边添加额外的信息。这样可以帮助我们解释一些容易误解的代码,Object-C代码基本是自解释的,所以您不是非常需要在IOS 和 OS X 应用添加太多的注释。

Variables 变量

变量是一个容器,他可以存储不同的值。在C语言中,变量类型是静态的,意思就是您必须清楚的声明,您想存储什么类型的值。声明变量的语法为: ,给变量赋值用=操作符。如果您想将一个变量转化成另一个类型,您可以在变量前添加括号,括号中添加新类型。

所有的都在下面的代码中演示了。声明了一个 odometer 变量,可以存储double类型的变量。 (int)odometer 的声明将转化odometer 存储的值 为一个int类型。如果您将代码粘贴在main.m文件中,运行程序。您将在输出面板中看到NSLog()中的消息。

// main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        double odometer = 9200.8;
        int odometerAsInteger = (int)odometer;

        NSLog(@"You've driven %.1f miles", odometer);        // 9200.8
        NSLog(@"You've driven %d miles", odometerAsInteger); // 9200
    }
    return 0;
}

像double和int一样,C语言定义了很多原生的数据类型。一个完整的列表可以在原生模块中找到。而且上面用到的the %.1f 和 %d 格式化符号也有解释。

Constants 常量

常量修饰符是告诉编译器,这个变量是不能被修改的。比如定义一个变量叫pi,如果您试图修改它,将会导致编译器报错。

double const pi = 3.14159;
pi = 42001.0;               // Compiler error

这个通常是用在函数的参数中,告诉函数的调用者,他们可以假设传递给函数的参数是不会被修改的。

Arithmetic 算数

我们熟悉的+, -, *, /符号是用来做基本的算数运算符,模运算符(%)被用来返回整数相除的余数。

下面是演示:

NSLog(@"6 + 2 = %d",  6 + 2);    // 8
NSLog(@"6 - 2 = %d",  6 - 2);    // 4
NSLog(@"6 * 2 = %d",  6 * 2);    // 12
NSLog(@"6 / 2 = %d",  6 / 2);    // 3
NSLog(@"6 %% 2 = %d", 6 % 2);    // 0

当涉及到浮点数或者整数操作时,要特别的小心。详细信息请看整数相除。

当您使用循环时,经常遇到++ 或者–操作符。他是一个从变量中减一或者加一的简易的符号。

int i = 0;
NSLog(@"%d", i);    // 0
i++;
NSLog(@"%d", i);    // 1
i++;
NSLog(@"%d", i);    // 2

Conditionals 条件

像其他的语言一样C语言也提供了标注的if声明。他的基本语法,以及一个用于描述逻辑运算符的表,如下所示:

int modelYear = 1990;
if (modelYear < 1967) {
    NSLog(@"That car is an antique!!!");
} else if (modelYear <= 1991) {
    NSLog(@"That car is a classic!");
} else if (modelYear == 2013) {
    NSLog(@"That's a brand new car!");
} else {
    NSLog(@"There's nothing special about that car.");
}

Operator Description
a == b Equal to
a != b Not equal to
a > b Greater than
a >= b Greater than or equal to
a < b Less than
a <= b Less than or equal to
!a Logical negation
a && b Logical and
a || b Logical or

C语言也提供了 switch 声明,但是参数只能是整数,不能是浮点数、指针或者其他的Object-C对象。与if条件语句相比较,相当的不灵活。

// Switch statements (only work with integral types) 
switch (modelYear) {
    case 1987:
        NSLog(@"Your car is from 1987.");
        break;
    case 1988:
        NSLog(@"Your car is from 1988.");
        break;
    case 1989:
    case 1990:
        NSLog(@"Your car is from 1989 or 1990.");
        break;
    default:
        NSLog(@"I have no idea when your car was made.");
        break;
}

Loops 循环

while 和 for 循环可以迭代一些值,相关的break 和continue可以相应使退出循环,或者跳过一个迭代

int modelYear = 1990;
// While loops
int i = 0;
while (i<5) {
    if (i == 3) {
        NSLog(@"Aborting the while-loop");
        break;
    }
    NSLog(@"Current year: %d", modelYear + i);
    i++;
}
// For loops
for (int i=0; i<5; i++) {
    if (i == 3) {
        NSLog(@"Skipping a for-loop iteration");
        continue;
    }
    NSLog(@"Current year: %d", modelYear + i);
}

现在是合适的时间介绍for-in循环,然而这并不是C语言的。这个称为快速枚举语法,因为相比传统的for和while循序,它是一种更加有效的迭代Object-C集合的方法,比如NSSet 和NSArray 。

// For-in loops ("Fast-enumeration," specific to Objective-C)
NSArray *models = @[@"Ford", @"Honda", @"Nissan", @"Porsche"];
for (id model in models) {
    NSLog(@"%@", model);
}

Macros 宏指令

Macros are a low-level way to define symbolic constants and space-saving abbreviations. The #define directive maps a macro name to an expansion, which is an arbitrary sequence of characters. Before the compiler tries to parse the code, the preprocessor replaces all occurrences of the macro name with its expansion. In other words, it’s a straightforward search-and-replace:

宏指令是一个定义符号常量和别名的底层方法。
// main.m
import

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        double angle = PI / 2;              // 1.570795
        NSLog(@"%f", RAD_TO_DEG(angle));    // 90.0
    }
    return 0;
}

This code snippet demonstrates the two types of C macros: object-like macros (PI) and function-like macros (RAD_TO_DEG(radians)). The only difference is that the latter is smart enough to accept arguments and alter their expansions accordingly.

Typedef

Typedef可以使我们定义一个新的数据类型或者重新定义一个已经存在的数据类型。下面演示一个无符号字符的typeof定义,我们可以使用ColorComponent 就像我们是用char、int、double和其他内建数据类型。

// main.m
import

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ColorComponent red = 255;
        ColorComponent green = 160;
        ColorComponent blue = 0;
        NSLog(@"Your paint job is (R: %hhu, G: %hhu, B: %hhu)",
              red, green, blue);
    }
    return 0;
}

typedef 通常被用作将struct或者enum转化成更加方便的数据类型。这个被演示在下面两个段落。

结构体

struct 像一个简单的、原生的C对象。它可以让你聚合一些变量在一个比较复杂的数据结构,但是它不提供面向对象编程的特性,比如方法。比如,下面的代码片段使用struct聚合了组成RGB的元素。注意,我们通过typeof,可以通过更加有意义的名字获取。

// main.m
import <Foundation/Foundation.h>

typedef struct {
    unsigned char red;
    unsigned char green;
    unsigned char blue;
} Color;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Color carColor = {255, 160, 0};
        NSLog(@"Your paint job is (R: %hhu, G: %hhu, B: %hhu)",
              carColor.red, carColor.green, carColor.blue);
    }
    return 0;
}

我们使用了{255, 160, 0}的初始化语法,来初始化新的carColor 结构体。这个复制的顺序和我们定义struct的顺序一样。而且我们可以通过点语法获取每个域。

枚举

enum 关键字被用于定义枚举类型,他是一个相关常量的集合。像struct,通过typedef定义个更加有描述性的名字。

// main.m
import <Foundation/Foundation.h>

typedef enum {
    FORD,
    HONDA,
    NISSAN,
    PORSCHE
} CarModel;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CarModel myCar = NISSAN;
        switch (myCar) {
            case FORD:
            case PORSCHE:
                NSLog(@"You like Western cars?");
                break;
            case HONDA:
            case NISSAN:
                NSLog(@"You like Japanese cars?");
                break;
            default:
                break;
        }
    }
    return 0;
}

以为myCar 变量被定义为CarModel 类型,所以他只能存储四种类型的枚举成员: FORD, HONDA, NISSAN, and PORSCHE。定义这些在一个枚举类型中,比使用字符串代表各种CarModel更加的可靠。

Cocoa 框架使用大量的枚举定义常量。比如NSSearchPathDirectory 定义OS X使用的 标准目录路径。Ry’s Cocoa的教程,数据存储章节中提供了大量的例子。

原生数组

因为Object-C是C语言的超级,它就可以访问C语言中的数组。Foundation 提供的NSArray 和 NSMutableArray 比C 语言的数组更加的方便好用。然而,对性能要求比较高的环境,C语言数组还是很有用的。

int years[4] = {1968, 1970, 1989, 1999};
years[0] = 1967;
for (int i=0; i<4; i++) {
    NSLog(@"The year at index %d is: %d", i, years[i]);
}

years[4] 声明分配了可以存储四个int值得连续内存空间。我们使用 {1968, …}的初始化语法初始化了数组,我们可以通过传递偏移在方括号中获取数组的元素值。

指针

指针是一个内存地址的引用。指针删除了一个抽象层、是你可以看到,数值是怎样存储的。这样需要两个工具。

引用操作符可以返回变量内存地址。这样你就可以创建一个指针。

解引用操作符可以返回内存地址的存储内容。下面演示了,怎样申明、创建、解除指针。注意一个指针就像定义一个通常的变量,但是就是在变量之前添加了*。

int year = 1967;          // Define a normal variable
int *pointer;             // Declare a pointer that points to an int
pointer = &year;          // Find the memory address of the variable
NSLog(@"%d", *pointer);   // Dereference the address to get its value
*pointer = 1990;          // Assign a new value to the memory address
NSLog(@"%d", year);       // Access the value via the variable

指针图形化的表示如下:

这里写图片描述

上面的例子,指针仅仅是一个非必须的变量的抽象。他的实际的用处,你可以在附近移动指针。尤其是遍历数组,一个连续的内存空间。比如线面,通过指针迭代数组元素。

char model[5] = {'H', 'o', 'n', 'd', 'a'};
char *modelPointer = &model[0];
for (int i=0; i<5; i++) {
    NSLog(@"Value at memory address %p is %c",
          modelPointer, *modelPointer);
    modelPointer++;
}
NSLog(@"The first letter is %c", *(modelPointer - 5));

当我们使用指针,++操作符将移动指针到下一个地址,我们可以输出地址通过含有%p修饰符NSLog。同样,–操作符被用作减指针到之前的地址。如上所示,您可以访问相对当前地址的任何地址。

空指针

空指针是一个特殊的指针,不指向任何地址。C语言中只有一个空指针。他的类型是NULL 宏。主要表示变量为空,变量不能有一个正常的值。比如所示,通过空指正置空一个指针。

int year = 1967;
int *pointer = &year;
NSLog(@"%d", *pointer);     // Do something with the value
pointer = NULL;             // Then invalidate it

我们表示year 为空变量,可以通过设置为0实现,但是,对于year也是一个正常的值,而不是缺少值。

Void 指针

void 类型的指针是一个通用类型,它可以指向任何地址。因此,我们需要将void类型的指针转化成非void类型的指针。比如, (int *)申明将指正转化成int类型的指正。

int year = 1967;
void *genericPointer = &year;
int *intPointer = (int *)genericPointer;
NSLog(@"%d", *intPointer);

void 类型指正提供了很多的灵活性。比如NSString 类定义如下方法,将C数组转化成Object-C的字符串。

- (id)initWithBytes:(const void *)bytes
             length:(NSUInteger)length
           encoding:(NSStringEncoding)encoding

bytes 参数指向C数组的内存第一个地址。length参数执行读取多少字节,encoding 参数怎样解析字节。使用void类型的指针,这样bytes参数可以是任何字符数组。

Objective-C 中的数组

这是一些背景知识,但是您日常的Object-C开发中并不需要。我们必须理解,Object-C中的每一个对象都是通过指针引用的。比如,NSString 类型变量必须存储一个指针,而不是一个基本的变量。

NSString *model = @"Honda";

空指针在Object-C和C中是不一样的,在C中使用NULL,Object-C定义可自己的宏,nil,他是空。重要经验是,在引用Object-C对象指针中使用nil,而对于C指针使用NULL。

NSString *anObject;    // An Objective-C object
anObject = NULL;       // This will work
anObject = nil;        // But this is preferred
int *aPointer;         // A plain old C pointer
aPointer = nil;        // Don't do this
aPointer = NULL;       // Do this instead

整个Object-C语法都是和指正相关的。定义了一对象指针之后,您基本就忘记了他是一个指针,因为使用和其他的变量一样。我们将通过本教程的大量例子理解清楚。

总结

本章主要讲解C语言的基础。主要是想让大家熟悉变量、条件、循环、结构体、枚举、和指针。这些工具是构成任何Object-C程序的基础。

Object-C依赖C的这些基础结构,而且给与直接添加C++代码的选择。为了告诉编译器,编译的是C、C++或者Object-C,您可以将源代码的文件扩展名修改为.mm。

Languages available to files with .m and .mm extensions

这个独特的语言特征,打开了整个C/C++的大门,这是对于Object-C开发者最大的恩惠。比如,您在开发IOS游戏,发现自己需要一个物理引擎,你可以使用注明的Box2D 包,不需要额外的工作。

下一章我们将通过学习函数,结束C语言的学习。在这之后,我们已经准备好学习Object-C类、方法、协议和其他面向对象的内容。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值