第2章 数据类型和运算符
2.1 注释
- 程序注释是源代码的一个重要部分,对于一份规范的程序源代码而言,注释应该占到源代码的1/3以上;
- 单行注释
//
; - 多行注释
/*
注释开始和*/
注释结束,需要指出的是多行注释不可以嵌套,在/**/
多行注释代码块内,不能再次使用/**/
添加多行注释;
2.2 标识符和变量
2.2.1 分隔符
Objective-C语言里的分号(;)、花括号({})、方括号([])、圆括号(())、空格、圆点(.)都具有特殊的分隔作用,因此被统称为分隔符。
- 分号
Objective-C语言里语句的分隔不是使用回车来完成的,而是采用分号(;)作为语句的分隔,因此,每个Objective-C 语句必须使用分号作为结尾。Objective-C 程序允许一行书写多行语句,每个语句之间以分号隔开即可,单从程序的可读性角度来看,应该避免在一行书写多个语句;一个语句也可以跨多行,只要在最后结束的地方使用分号结束即可。 - 花括号
花括号的作用就是定义一个代码块,一个代码块指的就是" {" 和" }" 所包含的一段代码,代码块在逻辑上是一个整体。 - 方括号
- 圆括号
- 空格
Objective-C 语言中的空格包含空格符(Space)、制表符(Tab) 和回车(Enter) 等。除此之外,Objective-C 源程序还会使用空格来合理缩进代码,从而提供更好的可读性。 - 圆点
2.2.2 标识符规则
标识符,就是用于给程序中变量、类、方法命名的符号。规则如下:
- 标识符可以由字母、数字、下画线(_)和美元符($)组成,其中不能以数字开头;
- 标识符不能是Objective-C 关键字,但可以包含关键字;
- 标识符不能包含空格;
- 标识符只能包含美元符($),不能包含@、#等其他特殊字符;
- Objective-C语言是区分大小写的。因此,abc 和Abe 是两个不同的标识符;
2.2.3 Objective-C 关键字
- auto
- break
- case
- char
- const
- continue
- default
- do
- double
- else
- enum
- extern
- float
- for
- goto
- if
- int
- long
- register
- return
- short
- signed
- sizeof
- static
- struct
- switch
- typedef
- union
- unsigned
- void
- volatile
- while
2.3 数据类型分类
Objective-C语言其实就是C语言的超集,因此,它支待的数据类型与C 基本相似,大致分类如下:
数据类型:
基本类型
- 整型
- 字符型
浮点型
- float类型
- double类型
- 枚举型
构造类型
- 数组类型
- 结构体类型
- 共用体类型
指针类型
- 指针类型既是C 语言最重要的数据类型,也是Objective-C 最重要的类型,所有的系统类、自定义类的本质都是指针类型。
- 还有一种特殊的空类型(null type), 空类型就是nil 值的类型,这种类型没有名称。因为空类型没有名称,所以不可能声明一个空类型的变量,或将变量转换成空类型。空引用(nil) 是空类型变量唯一的值。空引用nil可以转换为任何引用类型。
2.4 基本数据类型
2.4.1 整型
- short int(简称short):在内存中通常占16位,取值范围:-2^15 ~ 2^15 - 1;
- int:在内存中通常占32位,取值范围:-2^31 ~ 2^31 - 1;
- long int(简称long):在内存中通常占64位,取值范围:-2^63 ~ 2^63 - 1;
- long long:在内存中通常占64位,取值范围:-2^63 ~ 2^63 - 1;
需要指出的是,Objective-C 并没有硬性规定各种整型在内存中所占的空间。通常来说,Objective-C 要求long long 型所占用的内存空不小于long 型所占用的内存空间,long 型所占的内存空间应该不小千int 所占的内存空间,int 型所占的内存空间应该不小于short 型所占用的内存空间。
经验证,在64位Mac系统上,iPhone 8 Plus,short占2字节,int占4字节,long占8字节,long long占8字节。
size_t stSize = sizeof(short);
size_t intSize = sizeof(int);
size_t ltSize = sizeof(long);
size_t llSize = sizeof(long long);
NSLog(@"\nshortSize = %zu\nintSize = %zu\nlongSize = %zu\nlonglongSize = %zu",stSize,intSize,ltSize,llSize);
打印:
shortSize = 2
intSize = 4
longSize = 8
longlongSize = 8
Objective-C 中整数常量有3 种表示方式:十进制、八进制和十六进制,其中八进制的整
数常量以0开头,十六进制的整数以0x或者0X开头,其中10~15分别以a~f ( 此处的a~f不区分大小写)来表示。
// 以0开头的整数常量是八进制的整数
int octalValue = 013;
// 以0x或者0X开头的整数常量是十六进制的整数
int hexValue = 0x13;
NSLog(@"\noctalValue = %d\nhexValue = %d",octalValue,hexValue);
打印:
octalValue = 11
hexValue = 19
Objective-C 还允许在上述4 种整型前面添加unsigned 关键词,将它们变成无符号整型,无符号整型的最高位不是符号位,而是数值位,因此无符号整数不能表示负数。但与此同时,无符号整型的最大值比对应的普通整型的最大值大一倍。比如,short int 的取值范围是-2^15 ~ 2^15 - 1, 而unsigned short 的取值范围则是0~2^16 - 1。
2.4.2 NSLog输出函数及格式字符
使用NSLog()函数非常简单,它的第一个参数应该是一个字符串常量,该字符串中可使用%格式的占位符,这个占位符将会由对应的变量填充。
%格式的占位符如下:
- %d:以带符号的十进制形式输出整数(正数不输出符号);
- %o:以八进制无符号形式输出整数(不输出0 前缀);
- %x:以十六进制无符号形式输出整数(不输出0x 前缀);
- %u:以无符号十进制形式输出整数;
- %c:以字符形式输出,只输出一个字符;
- %s:输出C 风格的字符串;
- %f:以小数形式输出浮点数,默认输出6 位小数;
- %e:以指数形式输出浮点数,数字部分默认输出6 位小数;
- %g:自动选用%f 或%e 其中之一,保证输出宽度较短的格式,并且不会输出无意义的0;
- %p:以十六进制形式输出指针变量所代表的地址值;
- %@:输出Objective-C 的对象;
%zu:C99 ,输出 size_t 类型
/* 32位 */ typedef unsigned int size_t; typedef int ssite_t; /* 64位 */ typedef unsigned long size_t; typedef long ssize_t;
在%与格式字符之间,还可插入如下附加符:
- l (字母):可在格式字符d、o、x、u 之前,用于输出长整型整数,比如%ld;也可在f、e、g 之前,用于输出长浮点型数;
- m (代表一个正整数):指定输出数据所占的最小宽度;
- .n:对于浮点数,表示输出n 位小数;对于字符串,表示截取的字符个数;
- -:表示输出的数值向左边对齐;
int a = 56;
NSLog(@"==%d==" , a);
NSLog(@"==%9d==" , a); // 输出整数占9位
NSLog(@"==%09d==" , a); // 输出整数占9位,前边空位用0代替
NSLog(@"==%-9d==" , a); // 输出整数占9位,并且左对齐
NSLog(@"=%o==", a); // 输出八进制数
NSLog(@"==%x==", a); // 输出十六进制数
long b = 56;
NSLog(@"%ld" , b); // 输出long int 型的整数
double dl = 2.3;
NSLog(@"==%f==" , dl); // 以小数形式输出浮点数
NSLog(@"==%e==" , dl); // 以指数形式输出浮点数
NSLog(@"==%g==" , dl); // 以最简形式输出浮点数
NSLog(@"==%9f==" , dl); // 以小数形式输出浮点数,并且最少占用9位
NSLog(@"==%9.4f==" , dl); // 以小数形式输出浮点数,至少占用9位,小数点共4位
NSLog(@"==%9lf==" , dl); // 以小数形式输出长浮点数,并且最少占用9位
NSLog(@"==%9.4lf==", dl); // 以小数形式输出长浮点数,至少占用9位,小数点共4位
打印:
2019-02-17 16:02:37.414651+0800 StudyDemoProjectDemo[478:51240] ==56==
2019-02-17 16:02:47.162964+0800 StudyDemoProjectDemo[478:51240] == 56==
2019-02-17 16:02:47.162964+0800 StudyDemoProjectDemo[478:51240] ==000000056==
2019-02-17 16:02:50.546915+0800 StudyDemoProjectDemo[478:51240] ==56 ==
2019-02-17 16:02:53.770814+0800 StudyDemoProjectDemo[478:51240] =70==
2019-02-17 16:02:54.635277+0800 StudyDemoProjectDemo[478:51240] ==38==
2019-02-17 16:02:57.499029+0800 StudyDemoProjectDemo[478:51240] 56
2019-02-17 16:03:00.236972+0800 StudyDemoProjectDemo[478:51240] ==2.300000==
2019-02-17 16:03:02.469281+0800 StudyDemoProjectDemo[478:51240] ==2.300000e+00==
2019-02-17 16:03:03.746471+0800 StudyDemoProjectDemo[478:51240] ==2.3==
2019-02-17 16:03:06.426914+0800 StudyDemoProjectDemo[478:51240] == 2.300000==
2019-02-17 16:03:29.924144+0800 StudyDemoProjectDemo[478:51240] == 2.3000==
2019-02-17 16:03:38.167223+0800 StudyDemoProjectDemo[478:51240] == 2.300000==
2019-02-17 16:03:42.453096+0800 StudyDemoProjectDemo[478:51240] == 2.3000==
2.4.3 字符型
字符型通常用于表示单个的字符,字符常量必须使用单引号(')引起来。Objective-C 的每个字符只占一字节,因此,单个的Objective-C 字符变量并不支待中文字符,中文字符占2个字节。
- 直接通过单个字符来指定字符常量:例如'A'、'9'和'O'等;
- 通过转义字符表示特殊字符常量:例如'\n'、'\t'等;
转义字符如下:
\b
:退格符;\n
:换行符;\r
:回车符enter;\t
:制表符tab;\"
:双引号;\'
;单引号;\\
;反斜线;
不仅如此,char 类型的值也可直接作为整数型的值来使用,但它是一个8位的无符号整数,即全部是正数,取值范围是0-255。如果把一个在0~255范围内的int整数赋给char类型的变量,系统会自动把这个int整数当成char 类型来处理。
2.4.4 浮点型
浮点类型有3种:float、double 和long double, 一般来说,float 型占用4 字节,double型变量占8 字节;而long double 则占16 字节(iPhone 8plus测试是8字节);
Objective-C 的浮点数有两种表示形式
- 十进制数形式:这种形式就是平常简单的浮点数,例如,5.12, 512.0, .512。浮点数必须包含一个小数点,否则会被当成int 类型处理;
- 科学计数法形式:例如,5.12e2 (即5.12 10^2)、 5.12E2 (也是5.12 10^2);
必须指出的是,只有浮点类型的数值才可以使用科学计数形式表示。例如,51200 是一个int 类型的值,但512E2 则是浮点型的值。
除此之外,Objective-C 还提供了3 个特殊的浮点型数值:正无穷大、负无穷大和非数,例如,使用一个正数除以0.0 将得到正无穷大,使用一个负数除以0.0 将得到负无穷大,0.0除以0.0 或对一个负数开方将得到一个非数。必须指出的是,所有的正无穷大数值都相等,所有的负无穷大数值都相等;而非数不与任何数值相等,甚至和非数自己都不相等。
double d = 512000.f;
double num1 = d/0.0f;
double num2 = -d/0.0f;
double num3 = 0.0f/0.0f;
NSLog(@"正无穷大inf=%g",num1);
NSLog(@"负无穷大-inf=%g",num2);
NSLog(@"非数nan=%g",num3);
NSLog(@"正无穷大比较%d",num1==1/0.0f);
NSLog(@"负无穷大比较%d",num2==-1/0.0f);
NSLog(@"非数比较%d",num3==num3);
打印:
2019-02-17 18:06:51.406790+0800 StudyDemoProjectDemo[563:72926] 正无穷大inf=inf
2019-02-17 18:06:51.407105+0800 StudyDemoProjectDemo[563:72926] 负无穷大-inf=-inf
2019-02-17 18:06:51.407216+0800 StudyDemoProjectDemo[563:72926] 非数nan=nan
2019-02-17 18:06:51.407306+0800 StudyDemoProjectDemo[563:72926] 正无穷大比较1
2019-02-17 18:06:51.407387+0800 StudyDemoProjectDemo[563:72926] 负无穷大比较1
2019-02-17 18:06:51.407468+0800 StudyDemoProjectDemo[563:72926] 非数比较0
2.4.5 枚举型
枚举类型定义
enum season { spring = 4, summer, fall, winter }; // 使用枚举值 enum season mySeason; mySeason = summer; // 把枚举值当成无符号整数执行输出 NSLog(@"mySeason 的值:%u" , mySeason);
定义匿名枚举类型时直接定义变量
enum {male, female} me, you;
上面代码中定义了一个匿名枚举类型,并在定义该枚举类型时定义了两个变量:me和you,这两个变量都只能等于male 或female 其中之一。
关于枚举值的几点说明:
- 定义枚举时{}中列出来的枚举值,也称为枚举常量或枚举元素,这些枚举值不是变量,因此不能对它们赋值。实际上,每个枚举常量按它们的定义顺序,依次为0、l 、2、3,以此类推。当然也可显式指定枚举常量的值;
- 枚举常量的本质就是无符号整数,因此,枚举值可以用来比较大小。比较大小的规则就是它们实际的整数值;
- 枚举值的本质是无符号整数,因此,Objective-C 允许直接将整数值赋值给枚举变量,甚至可以直接把枚举变量当成整数使用,比如,用它们来参与四则混合运算;
需要指出的是,虽然可以用整数值来代替枚举值,但实际编程中尽量不要使用整数值来代替枚举值,也不要使用枚举值来代替整数值-毕竟枚举值具有更好的可读性。
2.4.6 BOOL类型
Objective-C 提供了一个BOOL 类型,BOOL 类型的值有YES 和NO 两个值,分别代表真和假。但需要指出的是,Objective-C 底层实际上使用signed char 来代表BOOL。而YES、NO两个值的底层其实就是1 和0。对于C 语言而言,所有的非零数都会被当成真,因此,YES 也会被当成真处理;而NO 的值是0, 因此,NO 也会被当成假处理。
2.5 类型转换
有两种类型转换方式:自动类型转换和强制类型转换。
2.5.1 自动类型转换
如果系统支待把某个基本类型的值直接赋给另一个基本类型的变量,则这种方式被称为自动类型转换。自动转换规则如下:
- 把整型类型(包括字符型)的变量和值赋值给浮点型变量,不会有太大的变化;
- 把浮点型类型的变量和值赋值给整型(包括字符型)变量,数值的小数部分会被直接舍弃;
当把取值范围大的变量值赋给取值范围小的变量时,可能发生溢出;
比如将int型变量33000赋值给short型变量时,short的取值范围是(-32768~32767),就会发生溢出,强制类型转换为16 位的short 类型,则需要截断前面16 位,只保留后16 位,余下16 位中最左边的是符号位。
当把取值范围大的变量或值转换为取值范围小的类型时,必须格外小心,因为非常容易引起信息丢失。
2.5.2 强制类型转换
强制类型转换的运算符是圆括号()。
// 将浮点型强制转换成int型进行计算
int a = (int)2.3 + (int)122.2;
2.5.3 表达式类型的自动提升
当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升:
- 所有的short 型、char 型将被提升到int 型;
- 整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型;
- 操作数的等级排列为short
2.6 运算符
Objective-C 语言中的运算符可分为如下几种:
- 算术运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- 位运算符
- 类型相关运算符
2.6.1 算术运算符
- +:加法运算符,+还可以作为字符串的连接运算符;
- -:减法运算符,还可以作为求负的运算符号;
- *:乘法运算符;
- /:除法运算法;
除法运算符有些特殊,如果除法运算符的两个运算数都是整数类型,则计算结果也是整数,就是将自然除法的结果截断取整,例如,19/4 的结果是4,而不是5。 - %:求余运算符;
它要求运算符两边的操作数都必须是整数,它的计算结果是使用第一个运算数来除以第二个运算数(不可以为0),得到一个整除的结果后,剩下的值就是余数。 ++:自加;
这是一个单目运算符,运算符既可以出现在操作数的左边,也可以出现在操作数的右边,但出现在左边和右边的效果是不一样的。如果把++放在左边,则先把操作数加1, 然后才把操作数放入表达式中参与运算,如果把++放在右边,则先把操作数放入表达式中参与运算,然后才把操作数加1, 看如下代码:int e = 1; int f = e++ + 6; NSLog(@"e = %d, f = %d",e,f); e = 1; f = ++e + 6; NSLog(@"e = %d, f = %d",e,f); 打印: 2019-02-17 19:09:44.544752+0800 StudyDemoProjectDemo[607:80323] e = 2, f = 7 2019-02-17 19:09:51.456103+0800 StudyDemoProjectDemo[607:80323] e = 2, f = 8
- --:自减;
也是一个单目运算符,效果与++基本相似,只是将操作数的值减1。
2.6.2 赋值运算符
Objective-C 使用" = " 作为赋值运算符。值得指出的是,赋值表达式是有值的,赋值表达式的值就是右边被赋的值。例如,NSString *str2 = str 表达式的值就是str。因此,赋值运算符支持连续赋值,通过使用多个赋值运算,可以一次为多个变量赋值:
int a;
int b;
a = b = 7;
Objective-C 虽然支持一次为多个变量赋值的写法,但这种写法会导致程序的可读性降低,因此不推荐这样写。
2.6.3 位运算符
Objective-C 支持的位运算符有如下:
- &:按位与;
- |:按位或;
- ~:按位非;
- ^ :按位异或;
- <<:左位移运算符;
>>
:右位移运算符;
第1运算数 | 第2个运算数 | 按位与 | 按位或 | 按位异或 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
**提示:
所有的数字在计算机底层都是以二进制形式存在的,原码是直接将一个数值转换成二进制数。但计算机以补码的形式保存所有的整数。补码的计算规则如下:正数的补码和原码完全相同,负数的补码是其反码+1;反码是对原码按位取反,只是最高位(符号位)保持不变。负数补码求原码:补码符号位不变,补码-1,再取反就是原码。**
例子:
非运算
short a1 = -5; NSLog(@"~a1=%d",~a1); 打印: ~a1=4 原理解析: -5的原码: 10000000 00000101 -5的反码: 11111111 11111010 -5的补码: 11111111 11111011 -5非运算: 00000000 00000100(符号位0是正数,数值=4)
异或运算
short b1 = 5; short c1 = 9; NSLog(@"b1^c1=%d",b1^c1); 打印: b1^c1=12 原理分析: 5的补码: 00000000 00000101 9的补码: 00000000 00001001 异或运算:00000000 00001100(12)
位移运算符:
左移运算符是将运算数的二进制码整体左移指定位数,左移后右边空出来的位以0 来填充;
右移运算符为:>>,对于>>运算符而言,把第一个操作数的二进制码右移指定位数后,左边空出来的位以原来的符号位来填充。即如果第一个操作数原来是正数,则左边补0; 如果第一个操作数原来是负数,则左边补1。
例子:
- 位移运算
NSLog(@"5<<2=%d",5<<2);
NSLog(@"-5<<2=%d",-5<<2);
NSLog(@"5>>2=%d",5>>2);
NSLog(@"-5>>2=%d",-5>>2);
打印:
5<<2=20
-5<<2=-20
5>>2=1
-5>>2=-2
原理分析:(以short类型为例)
5的原码: 00000000 00000101
5的补码: 00000000 00000101
5<<2: 00000000 00010100(20)
5>>2: 00000000 00000001(1)
-5的原码: 10000000 00000101
-5的反码: 11111111 11111010
-5的补码: 11111111 11111011
-5<<2补码: 11111111 11101100
-5<<2反码: 11111111 11101011
-5<<2原码: 10000000 00010100(-20)
-5>>2补码: 11111111 11111110
-5>>2反码: 11111111 11111101
-5>>2原码: 10000000 00000010(-2)
**特殊:
1<<0(1)
1<<1(2)
1<<2(4)
1<<3(8)**
**总结:
当进行位移运算时,不难发现,左移n位就相当于乘以2的n次方,右移则是除以2的n次方(如果不能整除,实际返回的结果为小于除法结果的最大整数)不仅如此,进行位移运算不会改变操作数本身,它只是得到了一个新的运算结果, 而原来的操作数本身是不会改变的。**
2.6.4 扩展后的赋值运算符
赋值运算符可与算术运算符、位移运算符结合,扩展成为功能更加强大的运算符。
+=
; x += y,对应x = x + y;-=
;*=
;/=
;%=
;|=
;&=
;^=
;<<=
;>>=
;x>>=y,对应x = x>>y;
2.6.5 比较运算符
比较运算符用于判断两个变量或常量的大小,比较运算的结果是整数值(1 代表真、0 代表假)
>
;>=
;- <;
- <=;
- ==;
如果进行比较的两个操作数都是数值型,即使它们的数据类型不相同,只要它们的值相等,都将返回1。例如,97==
'a'返回1; 5.0 = 5也返回1。 - !=;
如果进行比较的两个操作数都是数值型,无论它们的数据类型是否相同,只要它们的值不相等,都将返回1。
2.6.6 逻辑运算符
逻辑运算符用于操作两个布尔型的变量或常量。
&&
(与);必须前后两个操作数都是真才返回真,否则返回假。||
(或);只要两个操作数中有一个真,就可以返回真,否则返回假。!
(非);只需要一个操作数,如果操作数为真,返回假;如果操作数为假,返回真。需要指出的是,Objective-C 并没有提供表示真、假的布尔型数据,Objective-C 通常会用1 代表真,用0 代表假。除此之外,Objective-C 会把任意非0 的数值当成真,0 才会被当成假。!5的值为0。
^
(异或);当两个操作数不同时才返回真,如果两个操作数相同,则返回假。
2.6.7 逗号运算符
Objective-C 提供了逗号运算符,用于将多个表达式”连接”起来,而整个逗号表达式将返回最后一个表达式的值。语法格式如下:表达式1, 表达式2, 表达式3, ... , 表达式n
当我们需要将逗号表达式的值赋给指定变量时,千万不要忘了把整个逗号表达式用圆括号括起来。
int a = (3 * 4, 5 > 2)
a的值为1
需要指出的是,并不是所有出现逗号的地方都是逗号表达式,比如前面程序中常用的输出语句:NSLog(@"%d, %d" , a, b);, 这条输出语句中的逗号只是用于分隔向NSLog()函数传入的多个参数,并不是逗号表达式。
2.6.8 三目运算符
三目运算符(?:)语法格式:(expression) ? if-true-statement : if-false-statement;
三目运算符的规则是:先对逻辑表达式expression 求值,如果逻辑表达式返回真,则执行并返回第二个操作数的值,如果逻辑表达式返回假,则执行并返回第三个操作数的值。
2.6.9 运算符的结合性和优先级
Objective-C 语言中的大部分运算符也是从左向右结合的,只有单目运算符、赋值运算符和三目运算符例外,其中,单目运算符、赋值运算符和三目运算符是从右向左结合的,也就是它们是从右向左运算的。
运算符的优先级:
- 分隔符:
. () [] {} ->
; - 单目运算符:
++ -- ~ ! *(取变量运算符) &(取地址运算符) sizeof
; - 强制类型转换运算符:
(type)
; - 乘法/除法/求余:
* / %
; - 加法/减法:
+ -
; - 移位运算符:
<< >>
; - 关系运算符:
< <= > >=
; - 等价运算符:
== !=
; - 按位与:
&
; - 按位异或:
^
; - 按位或:
|
; - 条件与:
&&
; - 条件或:
||
; - 三目运算符:
?:
; - 赋值:
= += -= *= /= &= |= ^= %= <<= >>=
; - 逗号运算符:
,
;
虽然Objective-C 运算符存在这种优先级的关系,但并不推荐过度依赖这种运算符的优先级,否则会降低程序的可读性。因此注意以下两点。
- 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,应把它分成几步来完成。
- 不要过多地依赖运算符的优先级来控制表达式的执行顺序,这会降低程序的可读性,应尽量使用()来控制表达式的执行顺序。