有C++基础学OC有多快?10分钟搞定变量和基本数据类型👋

哈喽各位有C++基础的小伙伴们!如果你正准备入坑iOS开发,那恭喜你——OC的基本数据类型和变量这块,你几乎已经会了90%!因为OC本身就是C语言的超集,C++又是基于C扩展的,所以很多概念和语法都是完全通用的。今天我们就用最快的速度把剩下10%的差异点和坑点彻底搞懂,看完就能直接写OC的基础代码啦!

变量的声明与初始化 👋

概念解释

变量就像你电脑里的文件夹,先给它起个名字,指定它能存什么类型的文件,然后就能往里面放数据了。OC的变量声明遵循“类型+变量名”的格式。

C++类比

变量声明的语法结构和C++完全一致,唯一的核心区别是:OC的所有可执行代码必须写在@autoreleasepool{}块内部(main函数里),这是OC内存管理机制的要求,而C++的代码直接写在main函数里就行。

✅ Objective-C 完整代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool { // OC特有:所有可执行代码必须写在这里
        // 先声明,再赋值
        int age;
        age = 22;
        
        // 声明同时初始化(推荐写法)
        float height = 1.75f;
        char gender = 'M';
        
        NSLog(@"年龄:%d,身高:%.2f,性别:%c", age, height, gender);
    }
    return 0;
}

🆚 对应的 C++ 对比代码

#include <iostream>
using namespace std;

int main() {
    // 代码直接写在main函数里即可
    int age;
    age = 22;
    
    float height = 1.75f;
    char gender = 'M';
    
    cout << "年龄:" << age << ",身高:" << height << ",性别:" << gender << endl;
    return 0;
}

避坑指南

💣 坑点1:忘记把代码写在@autoreleasepool里
错误代码示例:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    int age = 22; // 错误:代码不在@autoreleasepool内
    NSLog(@"年龄:%d", age);
    return 0;
}

正确代码示例:参考上面的OC完整代码,所有可执行代码都包裹在@autoreleasepool{}中。

💣 坑点2:变量未初始化就使用
和C++完全一样,未初始化的局部变量会存储垃圾值,导致程序运行结果异常。
错误代码示例:

int score;
NSLog(@"分数:%d", score); // 输出随机垃圾值

正确代码示例:

int score = 0; // 初始化默认值
NSLog(@"分数:%d", score); // 输出0

核心基本数据类型详解 ✨

OC的基本数据类型完全继承自C语言,所以和C++的基本类型几乎100%一致,只有一个BOOL类型是OC特有的,需要特别注意。

2.1 整数类型(int、short、long、long long)

概念解释

用来存储没有小数的数字,不同类型的区别是占用内存大小不同,能表示的数值范围也不同。

C++类比

和C++完全一致,包括signed(有符号,默认)和unsigned(无符号)修饰符的用法,占用字节数也和C++相同(32/64位系统下int均占4字节)。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 100;
        short b = 32767; // short类型最大值
        long c = 100000L; // long常量加L后缀
        long long d = 10000000000LL; // long long常量加LL后缀
        unsigned int e = 200; // 无符号整数
        
        NSLog(@"int: %d, short: %hd, long: %ld, long long: %lld, unsigned: %u", a, b, c, d, e);
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int main() {
    int a = 100;
    short b = 32767;
    long c = 100000L;
    long long d = 10000000000LL;
    unsigned int e = 200;
    
    cout << "int: " << a << ", short: " << b << ", long: " << c << ", long long: " << d << ", unsigned: " << e << endl;
    return 0;
}

避坑指南

💣 坑点:整数溢出
和C++完全一样,当赋值超过类型能表示的最大值时,会发生溢出,结果未定义。
错误代码示例:

short maxShort = 32767;
short overflow = maxShort + 1; // 溢出,结果为-32768
NSLog(@"overflow: %hd", overflow);

正确做法:根据数值大小选择合适的类型,大数值优先使用long long

2.2 浮点类型(float、double)

概念解释

用来存储带小数的数字,float是单精度(占4字节),double是双精度(占8字节,精度更高)。

C++类比

和C++完全一致,默认的浮点常量(如3.14)是double类型,float常量需要加f后缀(如3.14f)。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        float piFloat = 3.14159f; // float常量必须加f后缀
        double piDouble = 3.141592653589793; // 默认是double类型
        
        NSLog(@"float精度: %.7f", piFloat); // 保留约6-7位有效数字
        NSLog(@"double精度: %.15f", piDouble); // 保留约15-16位有效数字
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    float piFloat = 3.14159f;
    double piDouble = 3.141592653589793;
    
    cout << "float精度: " << fixed << setprecision(7) << piFloat << endl;
    cout << "double精度: " << fixed << setprecision(15) << piDouble << endl;
    return 0;
}

避坑指南

💣 坑点:用float存储高精度小数导致精度丢失
错误代码示例:

float money = 99.99f;
NSLog(@"金额:%.2f", money); // 可能输出99.98,精度丢失

正确代码示例:

double money = 99.99; // 用double存储高精度小数
NSLog(@"金额:%.2f", money); // 输出99.99

2.3 字符类型(char)

概念解释

用来存储单个字符,本质是1字节的整数,存储的是字符的ASCII码值。

C++类比

和C++完全一致,字符用单引号包裹,双引号包裹的是字符串。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        char ch1 = 'A';
        char ch2 = 65; // 直接用ASCII码赋值,和'A'等价
        
        NSLog(@"ch1: %c, ASCII码: %d", ch1, ch1);
        NSLog(@"ch2: %c, ASCII码: %d", ch2, ch2);
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int main() {
    char ch1 = 'A';
    char ch2 = 65;
    
    cout << "ch1: " << ch1 << ", ASCII码: " << (int)ch1 << endl;
    cout << "ch2: " << ch2 << ", ASCII码: " << (int)ch2 << endl;
    return 0;
}

避坑指南

💣 坑点:用双引号包裹字符
错误代码示例:

char ch = "A"; // 错误:"A"是字符串,不是单个字符

正确代码示例:

char ch = 'A'; // 正确:单引号包裹单个字符

2.4 OC特有的布尔类型(BOOL)💡

概念解释

OC的BOOL类型用来表示真和假,取值为YESNO。但注意!BOOL本质上是typedef signed char BOOL;,也就是说它其实是一个1字节的有符号整数。

C++类比

和C++的bool类型有本质区别!C++的bool是真正的布尔类型,只能存truefalse两个值;而OC的BOOL本质是char,任何非0值都会被当作YES

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // OC的BOOL本质是signed char,YES=1,NO=0
        BOOL isSuccess = YES;
        BOOL isError = NO;
        
        NSLog(@"isSuccess: %d, isError: %d", isSuccess, isError); // 输出1和0
        
        // 非0值都会被当作YES
        BOOL flag1 = 2;
        BOOL flag2 = -1;
        NSLog(@"flag1: %d, flag2: %d", flag1, flag2); // 输出2和-1
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int main() {
    // C++的bool是真正的布尔类型,只能存true和false
    bool isSuccess = true;
    bool isError = false;
    
    cout << "isSuccess: " << isSuccess << ", isError: " << isError << endl; // 输出1和0
    
    // 其他值会被自动转换为1或0
    bool flag1 = 2; // 转换为true(1)
    bool flag2 = -1; // 转换为true(1)
    cout << "flag1: " << flag1 << ", flag2: " << flag2 << endl; // 输出1和1
    return 0;
}

避坑指南(OC最容易踩的坑!)

💣 坑点1:直接用整数赋值给BOOL,导致逻辑错误
错误代码示例:

int result = 3;
BOOL isTwo = result; // 本意是判断result是否等于2
if (isTwo) {
    NSLog(@"result等于2"); // 这里会执行!因为3是非0值
}

正确代码示例:

int result = 3;
BOOL isTwo = (result == 2); // 用比较表达式赋值,结果只能是YES或NO
if (isTwo) {
    NSLog(@"result等于2"); // 不会执行,逻辑正确
}

💣 坑点2:用== YES判断BOOL值
错误代码示例:

BOOL flag = 2;
if (flag == YES) { // YES是1,2不等于1,条件不成立
    NSLog(@"flag为真"); // 不会执行,但flag实际为真
}

正确代码示例:

BOOL flag = 2;
if (flag) { // 直接判断,非0即为真
    NSLog(@"flag为真"); // 正确执行
}

常量的定义方式 📌

常量就是一旦定义就不能修改的量,OC有两种常用的定义方式,和C++完全一致。

3.1 #define宏常量

概念解释

预处理阶段的文本替换,没有类型检查,直接把宏名替换成后面的内容。

C++类比

和C++完全一致,用法和注意事项都相同。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

#define PI 3.1415926
#define MAX(a,b) ((a)>(b)?(a):(b)) // 带参数的宏

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        double radius = 5.0;
        double area = PI * radius * radius;
        NSLog(@"圆的面积:%.2f", area);
        
        int x = 10, y = 20;
        NSLog(@"最大值:%d", MAX(x, y));
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

#define PI 3.1415926
#define MAX(a,b) ((a)>(b)?(a):(b))

int main() {
    double radius = 5.0;
    double area = PI * radius * radius;
    cout << "圆的面积:" << area << endl;
    
    int x = 10, y = 20;
    cout << "最大值:" << MAX(x, y) << endl;
    return 0;
}

避坑指南

💣 坑点:宏定义不加括号,导致运算优先级错误
错误代码示例:

#define ADD(a,b) a+b
int result = ADD(2,3) * 4; // 替换后是2+3*4=14,不是预期的20
NSLog(@"result: %d", result);

正确代码示例:

#define ADD(a,b) ((a)+(b)) // 每个参数和整个表达式都加括号
int result = ADD(2,3) * 4; // 替换后是((2)+(3))*4=20
NSLog(@"result: %d", result);

3.2 const修饰的常量

概念解释

有类型检查的常量,编译阶段确定值,存储在常量区,不能修改。

C++类比

和C++完全一致,const修饰的变量是只读的,定义时必须初始化。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

const int MAX_AGE = 120;
const double PI = 3.1415926;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"最大年龄:%d", MAX_AGE);
        NSLog(@"圆周率:%.7f", PI);
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

const int MAX_AGE = 120;
const double PI = 3.1415926;

int main() {
    cout << "最大年龄:" << MAX_AGE << endl;
    cout << "圆周率:" << PI << endl;
    return 0;
}

避坑指南

💣 坑点:试图修改const常量
错误代码示例:

const int MAX_AGE = 120;
MAX_AGE = 150; // 编译错误:只读变量不能被修改

正确做法:const常量定义时必须初始化,之后不能再修改。

数据类型转换 🔄

当不同类型的数据一起运算时,会发生类型转换,分为隐式转换和显式转换,和C++完全一致。

4.1 隐式类型转换

概念解释

编译器自动进行的转换,规则是“小范围类型向大范围类型转换”,避免数据丢失。

C++类比

和C++完全一致,转换顺序为:char → short → int → long → long long → float → double

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 10;
        double b = 3.14;
        double result = a + b; // int自动转换为double
        NSLog(@"result: %.2f", result); // 输出13.14
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    double b = 3.14;
    double result = a + b;
    cout << "result: " << result << endl;
    return 0;
}

4.2 显式类型转换(强制转换)

概念解释

程序员手动指定转换类型,用于把大范围类型转换为小范围类型。

C++类比

和C++的C风格强制转换完全一致,OC不支持C++的static_castdynamic_cast等新式转换语法。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        double pi = 3.14159;
        int intPi = (int)pi; // 强制转换为int,丢失小数部分
        NSLog(@"intPi: %d", intPi); // 输出3
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int main() {
    double pi = 3.14159;
    int intPi = (int)pi; // C风格强制转换
    // C++新式转换:int intPi = static_cast<int>(pi);
    cout << "intPi: " << intPi << endl;
    return 0;
}

避坑指南

💣 坑点:大类型转小类型导致数据丢失
错误代码示例:

long long bigNum = 10000000000LL;
int smallNum = (int)bigNum; // long long转int,数据丢失
NSLog(@"smallNum: %d", smallNum); // 输出随机值

正确做法:确保转换后的值在目标类型的范围内,否则不要强制转换。

变量的作用域与生命周期 🕒

变量的作用域指的是变量能被访问的范围,生命周期指的是变量从创建到销毁的时间,和C++完全一致。

概念解释

  • 局部变量:定义在函数或代码块内,作用域是当前代码块,生命周期是代码块执行期间。
  • 全局变量:定义在函数外部,作用域是整个程序,生命周期是程序运行期间。
  • 静态局部变量:用static修饰的局部变量,作用域是当前代码块,生命周期是程序运行期间,只初始化一次。

C++类比

和C++完全一致,包括static修饰符的用法和效果。

✅ Objective-C 代码

#import <Foundation/Foundation.h>

int globalVar = 100; // 全局变量

void test() {
    static int staticVar = 0; // 静态局部变量,只初始化一次
    int localVar = 0; // 局部变量,每次调用都重新初始化
    
    staticVar++;
    localVar++;
    
    NSLog(@"staticVar: %d, localVar: %d", staticVar, localVar);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test(); // 输出staticVar: 1, localVar: 1
        test(); // 输出staticVar: 2, localVar: 1
        test(); // 输出staticVar: 3, localVar: 1
        NSLog(@"globalVar: %d", globalVar); // 输出100
    }
    return 0;
}

🆚 对应的 C++ 代码

#include <iostream>
using namespace std;

int globalVar = 100;

void test() {
    static int staticVar = 0;
    int localVar = 0;
    
    staticVar++;
    localVar++;
    
    cout << "staticVar: " << staticVar << ", localVar: " << localVar << endl;
}

int main() {
    test();
    test();
    test();
    cout << "globalVar: " << globalVar << endl;
    return 0;
}

避坑指南

💣 坑点:全局变量命名冲突
和C++一样,全局变量在整个程序中可见,多个文件定义同名全局变量会导致链接错误。
正确做法:尽量少用全局变量,必须用时用static修饰,限制其作用域在当前文件内。

static int fileGlobalVar = 200; // 只在当前文件可见

总结 🎯

今天我们把OC的变量和基本数据类型全部过了一遍,核心要点其实就5个:

  1. 除了BOOL类型,OC的所有基本数据类型(int、float、double、char)和C++完全一致,语法和用法无缝衔接。
  2. OC的BOOL本质是signed char,判断时直接用if(flag),绝对不要用if(flag==YES)
  3. OC的所有可执行代码必须写在@autoreleasepool{}块内,这是和C++最大的语法区别之一。
  4. 常量定义推荐用const代替#define,因为const有类型检查,更安全也更容易调试。
  5. 类型转换、变量作用域和生命周期的规则和C++完全相同,之前学的知识直接套用就行。

怎么样,是不是感觉OC入门其实很简单?搞定了基础数据类型,下一篇我们就正式进入OC的面向对象世界,学习类和对象的定义,看看OC的类和C++的类有什么不一样。

更多推荐