ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

变量与数据类型全解析:从存储原理到实战避坑

变量与数据类型全解析:从存储原理到实战避坑 1. 先把“变量”这个事彻底说清楚1.1 变量到底是什么从存储单元开始理解很多初学者学编程第一课都是变量但恰恰是这个最基础的概念最容易让人似懂非懂。网上一搜全是“变量就是装数据的盒子”这话没错但太含糊了。我做了十来年开发带过不少新人发现对变量的理解深度直接决定后面学指针、学引用、学内存管理时痛不痛苦。往底层说变量本质上是内存中一块有名字的存储区域。你在代码里写一句count 10编译器或者解释器会帮你做几件事在内存里找一块足够大的空间、把数值 10 按规定的格式存进去、然后给这块空间起个名字叫 count。以后你再用 count程序就知道去那个地址取值。就这么简单但又远不止这么简单。用个生活化的类比变量就像你办公桌上贴了标签的收纳盒。标签写“发票”你就知道里面放发票换内容的时候把旧发票抽出来塞新发票进去盒子还是那个盒子标签不用换。不同的盒子大小不一样有的适合放文件有的适合放回形针——这就对应了不同的数据类型。这里有个关键点很多人一开始没意识到变量名不是数据本身而是数据的“门牌号”。你在代码里操作变量实际上是顺着门牌号去找内存里的真身。这个概念在学到指针、引用、对象传参的时候会反复用到到时候再回头看这个“门牌号”思路很多坑就都能提前避开了。1.2 为什么所有语言都把变量和类型绑在一起讲你去看任何一本编程入门书前三章必然是“变量与数据类型”这不是巧合。变量负责“在哪存”数据类型负责“怎么存、存多少、能做什么运算”两者是分不开的一对。数据类型解决的是三个核心问题第一这块内存要占多大空间。同样是整数有的语言里固定 4 个字节有的语言里根据数值自动伸缩。第二这块内存里的二进制怎么解释。同样一串 01 序列按整数解释可能是 65按字符解释就是大写字母 A。第三这个值能参与哪些运算。你不能拿一个字符串去开平方也不能把两个结构体直接相乘类型决定了游戏规则。所以业界常说“类型系统是编程语言的安全带”。你写了一个不合理的操作强类型语言在编译期就拦下来报错告诉你不许这么干弱类型语言虽然放行但运行期往往给你一个莫名其妙的结果。我见过太多新手在 Python 里踩“1” 1的坑报错信息直接红了脸也见过老手在写 C 的时候因为类型转换不当查了一下午的 bug。类型这事值得从头就认真对待。1.3 三种主流类型机制的差异静态、动态与强、弱这里想多聊几句因为网络上讨论变量和数据类型时最常见的概念混淆就是静态类型和强类型。有人以为静态类型等于强类型动态类型等于弱类型其实这是两套完全独立的维度。静态类型和动态类型说的是“什么时候知道变量的类型”。静态类型在编译阶段就确定了典型代表是 C、C、Java、Go你写int a 1编译器盯着你后续想把字符串塞给 a直接编译失败。动态类型则是在运行时才确定典型代表是 Python、JavaScript、Ruby同一个变量先装数字再装字符串完全没人管你。强类型和弱类型说的是“类型之间是否允许隐式转换”。强类型语言中不同类型的数据不能随便混用需要显式转换比如 Python 里1 1直接报 TypeError。弱类型语言里则宽松很多JavaScript 里1 1悄悄给你变成字符串11这种隐式转换有时候方便但更多的是惊吓。这两组概念交叉在一起就有了四种组合C 是静态弱类型Python 是动态强类型JavaScript 是动态弱类型Java 是静态强类型严格说也有争议。搞懂这两套维度再看网上吵架“Python 到底是不是强类型”你就有了自己的判断依据。反正我的经验是语言选型时不要只看强弱更要看团队习惯和项目阶段。2. 简单数据类型逐个拆解不只是背概念2.1 整数、浮点、字符和布尔四类基础成员的脾气几乎所有编程语言都内置这几类基本数据类型但每个语言在细节上都有自己的小九九。这里挑最核心的四种来说顺手把容易踩的坑标出来。整数类型英文是 integer。C 语言里分short、int、long区别在于占用的字节数不同Java 里更是连byte1 字节、short2 字节、int4 字节、long8 字节给你安排得明明白白。Python 比较省心整数随便多大都能存不用担心溢出代价是性能上不如定长的整数。这里要提醒一句很多语言里整数除法是直接截断的5 / 2在 C 里结果是 2 而不是 2.5Python 3 改成了真除法但5 // 2才是整数除法。这种细节往往就是 bug 的温床。浮点类型英文是 float。它用来表示小数但底层是二进制科学计数法所以很多十进制小数在计算机里根本没法精确表示。你试试 Python 里0.1 0.2输出是0.30000000000000004无数新手在这里怀疑人生。我当年第一次遇到这个问题还以为是电脑坏了。忠告是涉及金额计算千万别用浮点数用十进制类型或者整数换算成“分”来算。字符类型C 语言里用单引号包起来如A本质是一个整数编码。Java 和 C# 里的char是两字节的 Unicode 字符。Python 3 里没有单独的字符类型字符串就完事了。布尔类型只有true和false两个值但有很多语言里布尔值其实是整数扮的比如 C 语言里 0 是假、非 0 是真JavaScript 里布尔值和数字还能互相参与运算。这些语义差异跨语言开发时一定要当心。2.2 复合类型不过夜字符串其实是“特殊”的字符串这个类型很特殊。它看起来像基础类型实际上很多语言里它是引用类型或类类型。C 语言里没有原生字符串只能用字符数组或者指针所以hello的本质是一个以\0结尾的字符序列。C 有std::stringJava 有StringPython 有str都包装成了好用的东西但底层逃不开“字符数组”这个根。字符串最常见的坑是“不可变性”。Java 和 Python 里的字符串一旦创建就不能修改你对字符串做拼接、替换操作时实际上是生成了一个新的字符串对象。如果你在循环里拼命拼字符串性能会肉眼可见地下降。很多做过 Java 服务端的人都有这个经验循环里拼接字符串一定要用 StringBuilder直接拼接在循环里就是灾难。Python 里类似的问题是不要写result item这种循环拼接改成.join(list)效率能提升一个量级。字符串还有个细节是编码。早期的 ASCII 只有 127 个字符后来各国文字涌现才有了 Unicode。同一个字UTF-8 编码和 UTF-16 编码在内存里的字节序列完全不同。我见过太多因为编码不一致导致的乱码问题最典型的是打开网页看到一串“锟斤拷”那是 UTF-8 解码成 GBK 再转 UTF-8 的经典产物。处理字符串时心里始终要绷着一根编码的弦。2.3 字面量类型 vs 数据类型一字之差天壤之别热词里有个“字面量类型和数据类型的区别”值得拿出来单独讲。字面量是代码里直接写出来的值比如42、3.14、Hello、True它们属于某个数据类型但数据类型本身是一个更宽泛的概念。打个比方数据类型是“类”字面量是“具体的实例”。42这个字面量是整数类型的一个具体值你可以对它做所有整数能做的运算。但反过来整数类型不仅包含42还包含-7、1000000、0等等无穷多个值。在 TypeScript 这类支持字面量类型的语言里你甚至可以定义“只能是特定字面量值”的类型比如性别字段只能取male或female这在编译期就能拦截错误输入。理解这个区别对调试很有用。有时候报错说“类型不匹配”你盯着代码看了半天觉得明明是对的——其实问题出在你把字面量的类型搞错了。比如123和123在逻辑上不同一个是整数一个是字符串虽然视觉上只差一对引号。排错的时候先看看自己手写的字面量是什么类型往往能少走很多弯路。3. 变量的定义、赋值与作用域实操中真正要命的地方3.1 不同语言定义变量的语法对照变量定义这块各语言风格差异挺大但逻辑都是相通的。我把主流语言的写法列个对照表方便你横向对比记忆语言定义方式示例类型说明特点Pythoncount 10动态类型无需声明简洁但变量名拼错会悄悄新建变量Cint count 10;静态类型先声明后使用变量必须先定义类型不能变Javaint count 10;静态类型强类型声明和赋值可以分开JavaScriptlet count 10;动态类型ES6 后推荐 letvar 有变量提升的坑Gocount : 10类型推断:是声明赋值的简写Cint count{10};静态类型花括号初始化可以防止窄化转换Python 的动态类型看着省事但有个著名的坑变量名拼错时Python 不会报错而是偷偷给你创建一个新变量。比如你本意是total_price累加结果写成了total_pirce程序不会炸但结果完全不对。这种 bug 特别难查因为不报错、不提示只能靠肉眼盯。用 Python 写长一点逻辑时最好把变量集中定义在文件顶部或函数开头别边写边现造变量名。JavaScript 里的var有变量提升机制意思是变量声明会被提升到作用域顶部但赋值不会。这导致经典的“闭包陷阱”和“var 循环问题”。ES6 之后有了let和const作用域更符合直觉现在新项目里基本不用 var 了。如果你还在维护老代码遇到 var 的诡异行为先想想变量提升再说。3.2 把“赋值”和“引用”彻底分清这是新手进阶的分水岭。对于基础类型整数、浮点、布尔赋值就是简单地拷贝一份值。你写b a然后改ba不会变因为它们各自有独立的内存。但对于复合类型数组、对象、列表很多语言里赋值赋值的是“引用”而不是数据的副本。你写b ab和a指向同一块内存。你通过b修改数据a看到的是修改后的结果。这就像一个门牌号复制了两张但房子还是那一间。我见过无数人在这个点上翻车。最经典的案例是 Python 里复制列表a [1, 2, 3] b a b.append(4) print(a) # [1, 2, 3, 4]a 也被改了要真正复制一份独立的列表得用a.copy()或者a[:]切片。Java 里数组直接赋值也是引用要用Arrays.copyOf才行。理解这层原理之后很多“为什么我改了一个变量另一个也变了”的困惑就会瞬间消散。3.3 作用域规则变量不是在哪都能用的作用域决定了变量在代码的哪个范围里能被访问。全局变量在程序任何地方都能访问局部变量只在函数或代码块内有效。这个规则听着简单实际写代码时各种边界情况特别多。Python 的坑在于函数内赋值默认创建新局部变量即使外面有同名全局变量也不影响。想在函数内修改全局变量必须显式加global关键字。Java 和 C 的块级作用域{}内的变量也是新手容易踩的出了大括号你就访问不了了。JavaScript 的let也有块级作用域但var是函数级作用域这一字之差能让很多人困惑很久。实际工作中我基本要求代码里尽量少用全局变量。全局变量的问题是你根本不知道它在哪个地方被修改了排错时像大海捞针。如果要共享状态优先考虑传参、返回值、类属性这些显式途径。说得难听一点全局变量一时爽维护火葬场。这不是开玩笑大型项目里最难查的 bug往往就是某个全局变量被意外篡改了。3.4 变量命名看着不起眼影响却极大变量命名这个事说出来像说教但真的直接影响代码质量。一个好的变量名应该表意清晰让别人包括三个月后的自己看一眼就知道这个变量是干嘛用的。int a、string s这种命名写过几次就忘了含义害人害己。命名规范每个语言有自己的惯例Java 和 Go 用驼峰命名法totalPricePython 用下划线命名法total_price常量一般全大写加下划线MAX_COUNT。Python 还规定了下划线开头的特殊含义_name表示私有__name表示强私有名称改写__name__表示魔法方法比如__init__、__str__。这些约定俗成的规则不遵守不会报错但会让协作变得痛苦。我自己的经验是变量名宁可长一点也不要含糊。days_since_last_login比dsll好一百倍。现在的 IDE 都有自动补全长变量名不会增加多少打字量。还要避免使用内置函数名做变量名比如 Python 里你定义一个list [1,2]后面再用list()转换就炸了这种低级错误在面试题里出现频率极高。4. 类型转换、指针变量与复合数据类型进阶路上的关键节点4.1 强制转换和隐式转换到底谁说了算类型转换是每个程序员迟早要面对的课题。转换分成两类一类是隐式转换编译器或解释器帮你自动完成另一类是显式转换你写代码明确告诉程序“我要把这个类型变成那个类型”。隐式转换在表达式里经常发生。C 语言里int和float运算时int会悄悄转成float这叫整型提升或者寻常算术转换。但这个转换方向很重要从范围小的类型转范围大的类型是安全的比如int转double反过来就有风险比如double转int小数部分直接被截断甚至溢出变成垃圾值。我举个例子很多人写过这种代码int total 100; int count 3; double avg total / count; // 结果是 33.0不是 33.33因为total / count是两个整数相除结果还是整数 33然后才被转成 double 赋给 avg。要得到正确结果必须先转一个为浮点数double avg (double) total / count;。这种 bug 在统计计算、价格核算里非常常见而且不仔细看根本发现不了。Python 的类型转换比较直观用内置函数搞定int(123)把字符串转整数float(3.14)转浮点str(123)把数字转字符串。但字符串转数字有个坑转不了会抛异常比如int(abc)直接 ValueError。Java 的字符串转数字更麻烦Integer.parseInt(123)和Integer.valueOf(123)行为不一样前者返回基本类型后者返回包装类型。写工具代码的时候推荐优先用valueOf避免不必要的装箱拆箱。4.2 指针变量的认知模型别再死记硬背“指针就是地址”热词里出现了“指针变量”这是 C 和 C 学习的老大难。很多人被指针折磨得死去活来其实是学习方法出了问题——他们试图背概念而不是建立直觉。指针是一个变量它存储的是另一个变量的内存地址。也就是说普通变量存放数据指针变量存放“数据在哪”。定义指针用*int *p;读作“指向 int 的指针 p”。取地址用int a 10; int *p a;意思是 p 保存了 a 的地址。通过指针访问它指向的变量用解引用操作符*p相当于“去这个地址看看里面是什么”。我教新手时常用一个类比指针就像一张写着朋友家地址的纸条。纸条本身不占什么空间但它能带你找到朋友家。如果朋友搬家了地址变了纸条就失效了——这就是“悬空指针”。如果纸条上写的地址根本不存在你按着找过去就“段错误”——这就是野指针的经典崩溃。指针真正难的地方在于指针的指针、指针和数组的关系、指针运算这些叠加概念。但我跟你说只要把“指针存的是地址”这个底层直觉建立起来后面全都好办。建议自己动手画一画画个方框代表内存标注地址画个箭头代表指针指向多画几次就会了。工具方面VS Code 里调试 C 时可以看变量的内存视图比干看图直观多了。4.3 结构体与复合类型一次变量容纳多个字段结构体是把多个变量打包成一个新的复合类型C 语言里用struct关键字定义。比如你要描述一个学生有姓名、学号、成绩三个属性与其定义三个独立变量不如定义一个学生结构体struct Student { char name[50]; int id; float score; }; struct Student stu1; strcpy(stu1.name, 张三); stu1.id 1001; stu1.score 92.5;这样 stu1 就是一个能独立传递的“小档案袋”函数传参、数组存放都方便很多。结构体变量的定义有两种写法先定义类型再声明变量比如上面的示例或者在定义类型的同时直接声明变量struct Student { ... } stu1, stu2;。访问成员用点操作符stu1.score如果你拿到的是结构体指针则用箭头操作符p-score。结构体的内存布局也值得了解字段按声明顺序排列但为了对齐编译器可能在成员之间填充空白字节。比如一个char加一个int理论上占 5 字节实际因为对齐规则可能占 8 字节。这个细节在做底层开发、协议解析、文件读写时会非常关键——你用sizeof去量结构体结果往往比你手动算出来的大。Python 里没有结构体但可以用类class或者dataclass达到类似效果。Java 里就是普通的 class。语言不同思路殊途同归把相关的数据聚合在一起代码的组织性会好很多。4.4 Python 和 pandas 的类型转换数据分析场景的实战热词里出现了“pandas 数据类型转换”这是 Python 数据分析方向的高频操作。pandas 的 DataFrame 里每个 Series列有自己的 dtype常见的有int64、float64、object、datetime64等。分析师拿到原始数据第一步经常就是清洗和转换类型。最常见的转换方式是astype方法。比如把字符串列转成整数df[age] df[age].astype(int)把字符串转成日期df[date] pd.to_datetime(df[date])有几个坑必须提醒第一是astype(int)转换含有缺失值的列会报错需要先用dropna()或fillna()处理。第二是字符串列里混入了特殊字符比如“1,200”或者“未知”直接 astype 会失败需要先清洗。第三是to_datetime对格式多样的字符串可能会有歧义最好先用format参数指定格式否则日期解析可能出乎意料。还有一个细节pandas 读取 CSV 时默认会把纯数字列按int64或float64处理但有时候它猜不准。比如身份证号18 位数字在 Excel 里会被改成科学计数法在 pandas 里如果不指定dtypestr也可能被当成整数导致精度丢失。读文件时尽量显式指定列类型宁可多写几行代码也别让数据在类型转换时悄悄变了形。4.5 工具变量与双变量空间自相关专业场景里的“变量”热词里还有两个比较偏门的工具变量、双变量空间自相关。这俩不是编程里的变量是统计学和空间分析里的概念但碰巧出现在同一个热词列表里顺手也说两句。工具变量是计量经济学里的方法用来处理“内生性”问题。简单理解你想研究 X 对 Y 的因果效应但 X 可能和遗漏变量相关直接回归会得到有偏的估计。工具变量是两个阶段回归里的关键角色第一阶段用工具变量预测 X第二阶段用预测值回归 Y。注意一个红线工具变量必须满足“相关性”和“外生性”两个条件缺一不可。热词里提到“工具变量两阶段结果 R2 变小”的问题这在实证分析中很常见因为第二阶段用预测值回归解释力度往往不如原值这说明工具变量只解释了 X 的一部分变异不一定是坏事但要做弱工具变量检验比如 F 统计量是否大于 10。双变量空间自相关则是地理信息技术里的分析用来衡量两个变量在空间分布上的关联性。比如房价和绿化率到底是不是“高的地方都高、低的地方都低”。常用的指数是双变量 Morans I算出来正值表示正向空间集聚负值表示负相关。工具一般是 GeoDa 或者 ArcGIS操作上要先建空间权重矩阵再跑双变量 Moran 分析。这类空间统计概念对初学者不太友好但理解了“空间上相似的区域之间两个变量的关联”就算是入门了。这些“变量”虽然和编程里的变量含义不同但底层逻辑很像都是“给一个概念起个名字、确定它怎么度量、怎么参与后续计算”。学东西到最后都是相通的。5. 常见报错与排查速查表每个坑都是学费换来的5.1 变量未定义、类型不匹配、未声明变量说几个我自己踩过、也帮别人排查过的高频报错整理成表遇到的时候直接对照报错信息出现语言常见原因解决思路NameError: name x is not definedPython变量拼错、未定义就使用、作用域不对检查拼写确认变量在函数内还是全局UnboundLocalErrorPython函数内有赋值使用了全局变量名函数内加 global 声明或者换变量名variable might not have been initializedJava局部变量声明了但没赋值就用给变量赋初始值或用前先赋值use of undeclared identifierC/C没声明就用或声明在另一个作用域检查声明位置与作用域错误 800a01f4 变量未定义VBScript表单变量取错名或提交参数名不一致检查 Request.Form 的字段名是否与前端一致cannot find symbolJava变量名拼写错误或不在作用域IDE 里用查找功能定位变量声明处VBScript 那个 800a01f4 的报错是老版 ASP 开发里很经典的错误。c_submit_advance_这个变量未定义通常是因为前端的 name 属性和后端 Request.Form 取的名字不一致或者表单还没提交就访问了变量。排查思路是先在浏览器里右键查看提交参数再和后端代码比对。这种问题在成熟框架里基本不会遇到但老项目里还有可能出现遇到了也别慌慢慢对齐前后端字段名。5.2 类型相关的隐形陷阱不报错但结果不对比报错更难查的是那种“能运行但结果诡异”的问题。这类坑的共性在于语言没有强制拦截默认做的隐式转换不符合你的预期。典型场景一JavaScript 的宽松相等console.log(1 1) // true因为发生了隐式类型转换 console.log(1 1) // false严格相等值和类型都要求一致建议项目里统一用别用能少掉一堆莫名其妙的 bug。ESLint 配置里eqeqeq规则就是干这个的。典型场景二浮点数精度问题print(0.1 0.2 0.3) # False金额计算永远不要用浮点数用decimal.Decimal或者先转成整数“分”再算。判等的时候用abs(a - b) 1e-9这种允许误差的方式。典型场景三整数溢出和截断int a 2000000000; int b 2000000000; int c a b; // undefined behavior实际可能是个负数C 语言的 int 溢出是未定义行为结果不可预测。处理大整数运算时考虑用long long、BigInteger或者判断溢出后再处理。5.3 实战排查方法从“不知道哪错了”到“定位问题”遇到变量或类型相关的诡异问题有个通用排查路径按这个顺序来基本能定位第一看报错信息的前三行和最后三行。很多新手看报错只盯中间那一大段其实关键信息在头尾文件路径、行号、异常类型。Python 的 traceback 会清楚告诉你line 47, in module直接跳过去看那一行。第二打印中间变量。写临时print()或console.log()把每个关键变量的值打出来看它在哪一步开始变了、变错了。这是最笨但最好用的方法。别迷信调试器print 大法是排查变量的第一利器。当然配合调试器断点看变量的内存视图会更快比如 VS Code 的变量面板里能看到每个变量的当前值和类型。第三最小化复现。把有问题的代码一点点删删到只剩能触发问题的最小片段。如果删的过程中问题消失了说明触发条件就在被删的部分里。这个过程也能帮你写出一段清楚的代码发给别人求助比贴一整屏代码有效得多。第四用类型检查工具。Python 用 mypyJavaScript 用 TypeScript写 C/C 用编译器的-Wall -Wextra警告选项。很多类型问题在编译期就能被工具发现不要等运行期才炸。5.4 规避类型坑的最佳实践把问题消灭在编码阶段从我个人的项目经验来说变量和类型问题带来的调试成本占整个开发时间的三到四成。规避它们最有效的手段不是学会排查而是在写代码时就用好习惯挡住隐患。一个习惯是变量始终初始化。C 语言里不初始化直接使用的局部变量是未定义值可能是任何垃圾数字。定义变量时顺手给个初始值排错时只要看赋值在哪不用猜“它本来就是什么”。Java 的局部变量编译器强制初始化C 语言则靠自觉。另一个习惯是善用类型推断但不过度依赖。Go 的:、C 的auto、TypeScript 的let都能让代码简洁但要记得类型是确定的。auto推断出来的类型如果和你想的不一样后续操作会全乱。所以遇到复杂类型还是显式写出来更安全。再一个习惯是写单元测试的时候专门测类型边界。空字符串、0、负数、极大值、极小值、null、undefined这些极端输入最容易暴露变量和类型问题。接口入参校验里显式判断类型别让脏数据一路穿透到计算核心。6. 变量使用的进阶心法从写得对到写得好到这里变量和简单数据类型的基本盘已经聊完了。最后想分享几个偏“内功”的心得可能不那么立竿见影但长期看帮助很大。第一个心得是“变量的生命周期越短越好”。一个临时变量只在一个小作用域里使用用完就完这样它的状态空间小出问题也容易排查。尽量避免在文件顶部堆一大堆全局变量后面的函数都依赖它们牵一发动全身重构的时候痛不欲生。第二个心得是“一组相关变量优先打包成结构体或类”。如果你的代码里频繁出现user_name、user_id、user_score这种前缀重复的变量说明你应该定义成一个User结构体了。变量之间的关系应该是显式的而不是靠名字前缀来隐式关联。第三个心得是“删除无用变量和注释掉的代码”。版本管理用 Git 就行历史记录都在那里注释掉的代码只会干扰读者判断哪些是有效逻辑。变量同理定义了不用的变量留着只会增加噪音。很多 IDE 会灰显未使用的变量看到就删。第四个心得是“读别人代码的变量名比读逻辑更快地了解代码意图”。这其实是反向验证——当你看一段代码看不明白时先看变量名如果变量名表意清晰逻辑基本能猜个七八成如果变量名全是a、b、temp那这段代码大概率质量堪忧要格外小心。这也是为什么我一直强调命名它不只是风格问题更是信息传递的效率问题。最后讲一个我真实的经历。几年前维护一个老系统有个 C 语言函数三百行里面有个全局变量flag十几个地方都在读写它。有一天需求变更要加新逻辑我改动了对 flag 的赋值结果把一套完全不相关的流程也带歪了线上出了故障花了整整一个周末才定位到问题。后来我花了一天时间把这个全局变量改成函数参数传递把 flag 拆分成了几个语义明确的局部变量代码不仅清爽了之后两年再也没出过类似问题。这件事给我的教训很深刻变量本身的语法很简单但变量使用的方式直接决定了项目能走多远。变量和数据类型是编程的地基。地基没打牢后面学什么框架、什么架构都是空中楼阁。把这篇文章里提到的原理、细节、坑都吃透你写代码时的确定性会大大提升调试时间能省下一大截。如果有正在入门的朋友把这篇文章转给ta也许能少走很多弯路。
返回列表