C 基础

原文地址:http://rypress.com/tutorials/objective-c/c-basics

OC 可以说是C语言的一个超集,这样你可以无缝的和C语言结合编程也就是你可以这两种语言编写同一份代码文件。事实上OC语言的大部分核心内容还是依赖于C语言的,所以在学习OC之前与必要将C语言的知识重新梳理总结一下。

The relationship between Objective-C and C

上述图片给出了OC和C之间的关系。我们将在本节中讨论C语言(或者说OC)的注释、变量、控制等一些基本语法,同时我们也将讨论在指针这一个概念。掌握这些基本概念为我们后续讨论的OC的面向多想特性打好基础。

注释

在C语言中又两种注释方式,单行注释和块注释。单行注释既是以双斜杠开头,注释内容不能跨行。而块注释的方式可以注释多好内容,但是需要以/*开头以 */符号结束。

下面是这种注释方式的例子:

// This is an inline comment

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

因为注释掉的内容会被编译器忽略,所以你写任何内容都不会对程序本身造成影响。所以我们可以利用注释来为自己的代码逻辑附上备注。这样可以使你或者他人在看代码的时候搞清楚代码的真实意图;当然,OC被设计的时候一大特点就是自注释,所以你不应该在真实IOS和OS X项目的代码中加入过多的注释。

变量

变量既是各种实际值的容器。在C语言中,变量的类型是固定的,所以你需要讲不同类型的数据放入相应类型的变量中保存。想要声明一个变量,一般是使用<type> <name>语法,当要给一个变量赋值的时候则使用 = 号操作符。如果你需要改变一个变量类型来使用,你可以可以通过强转类型的方式处理。

为了更好的说明以上内容我们在将在下面的例子中进行讲解。在下述代码中我们声明了一觉叫做odometer的double型变量。(int)odometer语句将存储的double型值强转成int型。如果你运行此段代码的话,你应该能够看到通过 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(
    }
    ;
}

除了double和int类型,C语言中还定义了其他丰富的基本数据类型。全部的类型说明你可以访问Primitives章节,此章节中还会为你解释上述代码中使用的诸如%.1f 和 %d格式化的使用方法。

常量

通过const关键字修饰的变量既是常量,常量被定义后是不可被修改的,负责在编译阶段就会出错。比如下面的这个例子,定义了一个叫做pi的常量接着试图修改pi的值,这将导致编译错误:

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

常量通常用于函数的参数传递场景中, 以此保证参数不会被修改。

算术操作

在算术操作中基本的操作有我们熟悉的+-*/,此外还有取模操作符等。这些操作符的使用方法使用实例如下:

NSLog( + );
NSLog( - );
NSLog( * );
NSLog( / );
NSLog( % );    

需要特别注意的情况是当运算中既有小数又有整数时候结果可能和你预计的不一样,详细可参考Integer Division了解。

你可能经常会在一个循环块语句中遇到++和--操作。这两个运算符分别的作用是变量加1和减1的意思。例如:

;
NSLog(
i++;
NSLog(
i++;
NSLog(

条件判断

C语言中if关键字同样也是众多语言中的条件判断关键字。在使用上经常和下表描述的情况相结合,例如:

;
) {
    NSLog(@"That car is an antique!!!");
} ) {
    NSLog(@"That car is a classic!");
} ) {
    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,然而它只能和整数类型的变量结合使用。这样如果条件是浮点数,指针或者是对象的情况下就显得不是很灵活。

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

循环

while和for关键字用户处理循环逻辑,相关的break和continue关键字则分别用于跳出循环或者忽略此次循环。

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

在OC中引入了另外一种循环语法 for-in 操作。这个操作语法在OC中对于NSSet and NSArray等数据容器来说效率比while和for操作更加高效。

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

宏是表现为一种缩写的常量形式。#define关键字后面定义宏名称后面跟随的时宏内容,宏内容上没有任何要求。在编译之前宏名称部分会被宏内容替换掉,换句话就是简单的搜索宏名称,如过找到就替换为宏内容这么简单。

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

#define PI 3.14159
#define RAD_TO_DEG(radians) (radians * (180.0 / PI))

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

上述代码片段描述了两种宏定义语法:类类型的宏(PI)和函数型的宏(RAD_TO_DEG(radians))。他们之间的唯一差别就是后者或许更智能一点他会将宏名称部分的参数内容代入宏内容。

Typedef

typedef关键字可以使你定义一个新的数据类型或者重命名一个数据类型(注:只是给原有数据类型起了一个名字或者对已有名字的数据类型另外起了一个名字当马甲而已)。如下述代码片段中通过对于unsigned char类型重命名为ColorComponent,这样我们就可以使用ColorComponent定义变量了,但是实际上就是unsigned char的马甲而已,只是在代码中更加便于理解和使用方便和直观。

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

typedef unsigned char ColorComponent;

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

typedef关键字的使用是我们的代码看上去更加容易理解,它经常被用在转换结构体或者枚举类型上,关于这一点下面两小节讲座补充说明。

结构体

机构体就像一个简单的,原始的C类型的“类”。它能够让多个变量组成一个复杂的数据结构,但是不能提供任何OOP(面向对象)的特性例如方法等。举个例子,在下面的代码片段中使用到了一个结构体去描述了一个RGB颜色数据结构。同时注意到上小节提到了typedef使用使得这个结构体拥有了一个富有意义的别名。

// 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 = {, , };
        NSLog(@"Your paint job is (R: %hhu, G: %hhu, B: %hhu)",
              carColor.red, carColor.green, carColor.blue);
    }
    ;
}

上述代码创建了carColor并用数组{255, 160, 0}初始化了这个结构体。数组的值将以相同的顺序赋值给结构体中定义的变量。并且,就像你看到的,每一个变量都能以(  .)操作符去访问变量。

枚举

enum是申明枚举的关键字,他是相关联的常量的集合。像结构体,经常使用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;
        }
    }
    ;
}

既然myCar变量的类型为CarModel,那么这个变量只能被赋予CarModel中的常量值:FORD,HONDANISSAN, and PORSCHE。所以此例子中使用枚举的好处在于我们不必去担心是否变量被赋予了不再逻辑内的变量值,如果我们不小心将“MISSAN”付给了myCar,编译器马上就能发现错了,而如果不适用枚举,你可能只能运行时在能发现此问题。

Cocoa框架中很多地方都依赖枚举来使用常量。例如,NSSearchPathDirectory中定义的标准路径。

基本类型数组

既然OC是C的超集,那么OC就能使用C中的基本类型数据。一般地,框架中提供的高级类型的 NSArray 和NSMutableArray类比使用C语言中的数组类型更加方便;然而,基本类型数组依然具有较好的性能优势。他们的语法参考如下:

] = {, , , };
years[] = ;
; i<; i++) {
    NSLog(@"The year at index %d is: %d", i, years[i]);
}

int years[4]语句申请了一段连续的内存去存储4个长度的int类型变量。让后使用{1968, ...}去初始化了这个数组,然后我们可以使用游标去访问数组值(例如:years[i])。

指针

指针是值就是内存的地址。如果说一个变量抽象出了一个具体的值,指针则是去掉了这个抽象而将真实的内存内容展现给你。指针的操作需要两个新工具:

  • 使用指针操作符号(&)能够获得一个变量的内存地址。这项相当于你创建了一个指针变量。
  • 使用(反)指针操作符号(*)则将指针地址(内存)中的数据转换成一个抽象的变量,便于你的访问与操作。

下面的例子中充分说明了怎样去申明,创建和转换指针的操作。值得注意的是定义一个指针就像定义一个普通变量一样,除了使用到了(*)操作符号。

;          // 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 = ;          // Assign a new value to the memory address
NSLog(@"%d", year);       // Access the value via the variable

指针的操作可以抽象成下面的图文:

在上述的例子中,指针貌似并没有起到什么作用,只是取代抽象了变量,感觉没有太多意义。其实指针的作用不止如此,在实际使用中我们可以通过指针的移动来方便的直观的达到我们的逻辑目的。特别是使用在那些内存连续的数组变量上。例如,下面的代码片段使用了指针来迭代了整个数据元素。

] = {'H', 'o', 'n', 'd', 'a'};
];
; i<; i++) {
    NSLog(@"Value at memory address %p is %c",
          modelPointer, *modelPointer);
    modelPointer++;
}
NSLog());

可以看到,利用++操作我们讲指针移动到下一个元素的内存地址,然后通过NSlog()方法来证明了指针移动的正确性,放过来--操作能够让指针移动到前一个元素的地址。在最后一行打印出来的信息中看出指针出来一个一个的元素移动外还具有任意的移动能力。

null指针

null指针是一个非常特殊的指针值,它不指向任何内存地址。在C语言中以宏的方式定义了一个空指针NULL。空指针代表了某个地址(既某个变量)无效。下面的代码片段解释了空指针的无效化的使用方法。

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

一般的认为将year设定为一个无效的值的话可以赋给它0,但是注意,0其实也是一个有效的“年”的值,不是吗?所以我们可以将year这个变量的内存指向空指针,这样year就无效化了。

Void 指针

void指针一个可以指向任何变量的指针,换句话说能够指向任何类型变量的地址的。因此,因为如果要使用void指针我们就需要更多的信息去解读翻译他,不然由于不知道它指向的何种类型的变量我们根本没有办法使用它。最最简单的方式就是通过强转将她转换成一个已知类型的指针。例如,(int *)语句将一个void指针转变成了一个整数类型变量的指针。

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

viod指针为我们提供了很多灵活性。例如,NSString类中定义了如下方法去讲C类型的数据转换成OC类型的字符串对象:


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

bytes参数指向了一个任何类型的C数组的首地址,length参数则定义了读取多少byte,encoding参数决定了以何种编码去解析。使用void指针就能够让此方法适用于任何类型的字符数组。不然我们只能分门别类的去定义方法解读single-byte, UTF-8, and UTF-16类型的字符数组。

OC中指针

This is all good background knowledge, but for your everyday Objective-C development, you probably won’t need to use most of it. The only thing that you really have to understand is that all Objective-C objects are referenced as pointers. For instance, an NSString object must be stored as a pointer, not a normal variable:

我们已经拥有了很好的指针的知识,但是在日常OC编程中你或许很少用到C的指针使用方法。但是你要记住所有的OC对象其实都是指针。例如,NSString对象变量必须申明为指针,并不是一个普通变量类型:

NSString *model = @"Honda";

表示空指针的方式C和OC还是有区别的,C使用NULL空表示,但是OC定义了自己的宏:nil,什么时候使用null什么时候使用nil呢,下面给出一般的规则:

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

不管如何声明OC变量,要记住所有的OC对象都是指针。所以你在OC的世界里你不要太过于强调指针的概念,因为没有东西不是指针,随着之后的教程例子你讲越来越习惯他。

总结

This module introduced the fundamental aspects of the C programming language. While you’re not expected to be a C expert just yet, we hope that you’re feeling relatively comfortable with variables, conditionals, loops, structs, enums, and pointers. These tools form the foundation of any Objective-C program.

这个模块中我们介绍了基础的C语言知识。你不必是一个C语言专家,我们只是希望你能对于变量,条件,循环,结构体,枚举和指针能有一个清晰的了解。这些构成了OC的基础内容。

OC依赖于C中的这些基本结构,但是OC也提供了另外一个选择C++语言,你可以直接将C++代码作为你的程序的一部分。不管是C,OC还是C++都可以构成你的源文件,当然你需要做的时告诉编译器当前源文件是何种语言。怎样通知编译器呢?很简单,不同类型语言的源文件给出不同后缀文件名,存在C++语言的源文件你需要使用.mm后缀名,而只存在OC和C的源文件使用.m后缀名。

Languages available to files with .m and .mm extensions

OC的这样先天优势使得你打开了C/C++生态系统的大门,有利于OC开发者能够使用丰富的C/C++资源。例如,如果你在编写一个IOS游戏,你需要使用到物理驱动,你可以直接使用有名的Box2D l库(使用C++编写)而不需要做其他额外工作。

接下来的模块中我们将通过简单介绍C语言的函数来结束对于C的讨论。之后我们讲着重讲解OC的类,方法,协议和剩下的OC的面向对象知识。

2.C 基础的更多相关文章

  1. java基础集合经典训练题

    第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...

  2. node-webkit 环境搭建与基础demo

    首先去github上面下载(地址),具体更具自己的系统,我的是windows,这里只给出windows的做法 下载windows x64版本 下载之后解压,得到以下东西 为了方便,我们直接在这个目录中 ...

  3. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  4. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

  5. [C#] C# 基础回顾 - 匿名方法

    C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...

  6. HTTPS 互联网世界的安全基础

    近一年公司在努力推进全站的 HTTPS 化,作为负责应用系统的我们,在配合这个趋势的过程中,顺便也就想去搞清楚 HTTP 后面的这个 S 到底是个什么含义?有什么作用?带来了哪些影响?毕竟以前也就只是 ...

  7. Swift与C#的基础语法比较

    背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...

  8. .NetCore MVC中的路由(1)路由配置基础

    .NetCore MVC中的路由(1)路由配置基础 0x00 路由在MVC中起到的作用 前段时间一直忙于别的事情,终于搞定了继续学习.NetCore.这次学习的主题是MVC中的路由.路由是所有MVC框 ...

  9. .NET基础拾遗(5)多线程开发基础

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

  10. .NET 基础 一步步 一幕幕[面向对象之方法、方法的重载、方法的重写、方法的递归]

    方法.方法的重载.方法的重写.方法的递归 方法: 将一堆代码进行重用的一种机制. 语法: [访问修饰符] 返回类型 <方法名>(参数列表){ 方法主体: } 返回值类型:如果不需要写返回值 ...

随机推荐

  1. Codeforces VK Cup Finals #424 Div.1 C. Bamboo Partition(数论)

    题目要求符合以下条件的最大的d 化简得 注意到 最多只有2*sqrt(a[i]-1)种取值,也就是一共最多有n*sqrt(10^19)种取值,于是枚举一下d,计算出符合上上式的最大的d更新答案,然后d ...

  2. NOIP2009 codevs1173 洛谷P1073 最优贸易

    Description: 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分为双向通 ...

  3. 【learning】杜教筛求欧拉函数前缀和

    我们考虑利用\(\sum\limits_{d|n}\varphi(d)=n\)这一性质来处理这个问题 设\(f(n)=\sum\limits_{i=1}^{n}\varphi(i)\) 那么我们可以得 ...

  4. Mysql 语句优化技巧

    前言 有人反馈之前几篇文章过于理论缺少实际操作细节,这篇文章就多一些可操作性的内容吧. 注:这篇文章是以 MySQL 为背景,很多内容同时适用于其他关系型数据库,需要有一些索引知识为基础. 优化目标 ...

  5. JavaScript URL汉字编码转换

    在使用url进行参数传递时,经常会传递一些中文名的参数或URL地址,在后台处理时会发生转换错误.在有些传递页面使用GB2312,而在接收页面使用UTF8,这样接收到的参数就可能会与原来发生不一致.使用 ...

  6. Java API不能远程访问linux服务器HBase的问题

    今天我在虚拟机里面安装了Hbase 1.2.4,说在windows上Java API调用访问下玩玩,结果始终连接不上. 现象是启动程序后,程序出现卡死的状态,没报错也不停止,大约半分钟后才打印一堆日志 ...

  7. BZOJ:2460[BeiJing2011]元素 (异或基+贪心)

    2460: [BeiJing2011]元素 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2910  Solved: 1535 题目链接:https: ...

  8. 关于equals与hashcode的重写

    我想写的问题有三个: 1.首先我们为什么需要重写hashCode()方法和equals()方法 2.在什么情况下需要重写hashCode()方法和equals()方法 3.如何重写这两个方法 **** ...

  9. SSH客户端,FinalShell服务器管理,远程桌面加速软件,支持Windows,Mac OS X,Linux,版本2.6.3.1

    FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求. 用户QQ群 342045988 Windows版下载地址:http:/ ...

  10. 基于tcp交互的python聊天程序

    语言:Python 工具:MySQL,Tkinter,图灵机器人 功能:图形聊天工具,可以选择自动回复或者人工回复. 注意:如果运行需要自建mysql数据库表.还有安装各种模块.还有到“图灵机器人”申 ...