MATLAB零基础系列的第二篇。上一篇我们把软件装好、界面认了个大概,这回来搞真正动手写代码之前必须搞明白的东西——变量和数据类型。说白了,你今天写的每一行MATLAB代码,本质上都是在跟变量打交道。家里有没有那种带标签的收纳箱?MATLAB的变量就是类似的玩意儿——你在箱子里放东西,然后在箱子上贴个标签,以后你只需要喊标签名字,就能把东西拿出来用。这篇的目标很简单:让一个完全没写过代码的人,看完之后能自己在命令行窗口里创建变量、修改变量、搞清楚这个变量是什么类型,并且知道为什么MATLAB有时候会跟你抱怨“类型不对”。整篇都是大白话加实操,建议打开你的MATLAB跟着敲一遍。
1. 先搞清楚变量到底是个什么东西
1.1 创建变量就是给数据贴标签
打开MATLAB,你会看到一个巨大的命令行窗口,光标一闪一闪等着你输入。试着敲下面这行,然后按回车:
a = 10窗口里立刻显示a = 10。恭喜,你刚刚创建了人生中第一个MATLAB变量。
注意中间那个等号。数学里它表示“等于”,但在MATLAB里,它的含义是“把右边的值装进左边这个名字里”。所以a = 10的意思是:在内存中找一块空间,存入数值10,然后把这块空间命名为a。以后你只要写一个a,MATLAB就知道你要的是那个10。
如果你不想让结果刷屏,只需要在语句末尾加一个分号:
a = 10;这条命令执行后,窗口里什么都不显示,但变量a已经存在了。分号是新手最容易被忽略的习惯。当年我刚学时,前两周一跑脚本就满屏飘数字,后来才知道全是分号惹的祸。赋值语句末尾加分号,是一个值得从第一天就养成的肌肉记忆。
1.2 变量会覆盖,清空要小心
继续操作:
a = 20;这时候a里的10就被20覆盖了。同一个变量名,同一时刻只能存一份数据,你给a赋了新值,旧值就没了。
再来一次:
a = [1 2 3];a又从数值变成了一个1行3列的数组。这说明MATLAB变量的类型可以随赋值内容随时改变,这点和Python有些像,和C语言那种定义好类型就不能变的思路完全不同。换句话说,你不需要专门写一句“声明类型”的话,赋值即创建、赋值即定类型。
想删除某个变量,用clear a;想把所有变量一次性全删掉,用clear或clear all。这里我要多说一句:很多老手其实不推荐你没事就clear all,尤其你在调试一个比较大的脚本时,把工作区清空等于把你之前算好的中间结果全扔了,万一没存盘,只能从头再跑。更稳妥的做法是clear 变量名逐个清理。
1.3 命名规则:别踩这3个雷
变量命名是零基础最容易犯错的环节。规则其实就三条:
- 必须以字母开头,不能是数字开头。
1a在MATLAB里是非法名字; - 只能由字母、数字、下划线组成,不能有空格、点号、加减号;
- 不能和MATLAB的关键字冲突,比如
if、for、while、end这些都不能当变量名。
另外注意,MATLAB是区分大小写的。a和A是两个完全不同的变量。这个问题害了无数新手——你在第五行定义了Data,然后在第二十行写data,MATLAB立刻告诉你“未定义变量”,你找了半小时才发现只是大小写对不上。
我建议命名的时候用有意义的单词组合,比如imageData、maxTemperature,别用x1、x2、a1、b2这种毫无信息的名字。等你的脚本写到一百行,你就会明白好名字有多重要。想检查某个名字能不能用,MATLAB里有个函数isvarname:isvarname('1a')返回0,isvarname('data_1')返回1。
顺便说一句,MATLAB没有C语言那种指针变量概念,你不需要关心变量在内存里的具体地址,变量名就是你访问数据的直接方式,这让很多事情变简单了。
1.4 认识一下那个默认变量 ans
有个小细节新手一定会遇到:你在命令行直接输入一个表达式然后回车,比如:
5 + 3结果8会显示在屏幕上,同时被存进一个叫ans的变量里。你可以输入ans回车查看它,确实是8。
但注意,ans是个偷懒变量。你下次再计算2 * 9,ans就被18覆盖了。所以真正写脚本的时候,永远不要把重要的中间结果寄存在ans里,它只适合你随手验算几个数。
2. MATLAB里到底有哪些数据类型
2.1 数值类型:double是默认,整数类型要主动指定
MATLAB里有一条根深蒂固的规矩:所有数值默认都是 double 类型。double 全称是“双精度浮点数”,占8字节,能表示极大范围的小数和整数,也是绝大多数计算用到的类型。写x = 3.14,x就是 double;写y = 100,y也是 double,哪怕100是个整数,它照样占8字节。
除了 double,还有 single(单精度,4字节)、int8、uint8、int16、uint16、int32、uint32、int64、uint64 这些整数类型。名称里的 int 表示有符号整数,uint 表示无符号整数,后面的数字代表位数。位数越大,能表示的数值范围越大,占用的内存也越大。
为什么要搞得这么复杂?直接全部用 double 不就行了?答案在于内存和场景。一张256×256的灰度图像,如果所有像素都用 double 存,需要 256×256×8 字节,约512KB;如果用 uint8 存,只要64KB,相差8倍。图像处理,尤其是视频流处理的时候,这种内存差距是致命的。所以很多图像处理库的默认数据格式就是 uint8,范围0到255,正好对应灰度值。
用一个表格来总结最常用到的几类数值类型,你可以截图存着:
| 类型 | 占用内存 | 表示范围 | 常见用途 |
|---|---|---|---|
| double | 8字节 | 约±1.8e308 | 默认的万能计算类型 |
| single | 4字节 | 约±3.4e38 | 大规模矩阵压缩内存 |
| uint8 | 1字节 | 0~255 | 图像像素、串口数据 |
| int8 | 1字节 | -128~127 | 小范围带符号数据 |
| uint16 | 2字节 | 0~65535 | 传感器原始数据 |
| int32 | 4字节 | 约±21亿 | 文件格式、一般计数 |
创建这些类型的方法很简单,调用对应函数:single(3.14)、uint8(255)、int16(-100)。如果你把一个浮点数丢进uint8()括号里,比如uint8(3.7),它会四舍五入变成4。如果把负数转成 uint8,比如uint8(-1),结果直接变成0,因为 uint8 表示不了负数。这种溢出行为新手容易懵,记住一句话:类型转换之前,先想想目标类型的范围够不够。
2.2 逻辑类型:比较运算的产物
逻辑类型是MATLAB的 boolean,只有两个取值:true 和 false,在内部存储中分别对应数值1和0。你只要写一句比较运算,结果就是逻辑类型:
age = 18; isAdult = age >= 18; result = 5 > 3;isAdult和result的类型都是 logical。逻辑值最常见的使用场景有三个:作为 if 条件、作为矩阵索引(mask索引在图像处理里特别常用)、以及和其他逻辑函数配合。你可以用logical(1)、logical(0)自己创建,也可以把任意非零数值转成逻辑真。注意MATLAB里0是假、非0为真,这个规则和很多编程语言一致。
2.3 字符和字符串:单引号双引号不是一回事
字符类数据类型是MATLAB新手最容易混淆的一块。在R2016b之前,MATLAB只有 char 类型,用单引号括起来,本质上是一个字符数组。比如:
str = 'hello';str是一个1×5的字符数组,你可以像操作数组一样操作它,比如str(1)返回'h'。这其实是一个历史包袱,MATLAB最初设计时就把字符串当成字符的数组来用。
从R2016b开始,MATLAB新增了 string 类型,用双引号括起来:
str2 = "hello";str2是一个真正的字符串对象,一个元素就是一个完整的字符串。换句话说,'hello'在内存里是5个字符排成一排,而"hello"是一个整体。两者之间用char()和string()可以互转,但如果把 char 和 string 混在一起操作,经常会出现你意想不到的结果。我的建议是:2020年之后的MATLAB版本,新代码里直接用 string 类型,省心;但如果你的代码要和第三方库、旧代码配合,还是老老实实跟着旧风格走。
这里有个经典例子,注意看:
'A' + 1结果是66。因为单引号的'A'是字符,参与加法运算时自动转成了对应的ASCII码65,再加1得到66。如果你用"A" + 1,行为完全不同。所以记住:char 能当数值算,string 不能。
2.4 容器类型:cell、struct和table
数值、逻辑、字符这些是“基础材料”,接下来这三个是“容器”,专门用来装不同类型的数据。
元胞数组cell,用花括号{}创建,能装任意类型、任意尺寸的数据:
c = {1, 'hello', [1 2 3]; true, 3.14, "world"};这里c是一个2行3列的元胞数组,第1行第1列是数值1,第1行第2列是字符串,互不干扰。访问时有个世纪难题:c(1)和c{1}有什么区别?c(1)取出的是一个1×1的元胞,它本身还是 cell 类型;c{1}取出里面装的实际内容。新手经常在这两种括号上翻车,我的记忆技巧是:花括号是“把锁撬开拿里面的东西”,圆括号是“隔着箱子看包裹”。
结构体struct,用点号访问字段:
student.name = '张三'; student.score = 98; student.id = 2024001;这里student是一个结构体,有三个字段 name、score、id。你还可以创建结构体数组:
student(2).name = '李四'; student(2).score = 87;这样student就变成了1×2的结构体数组。结构体特别适合把一组相关但不同类型的数据包在一起,比如一张图片的像素矩阵、标题、拍摄时间,用 struct 一包,传递起来非常省事。这就是热搜词里“结构体变量的定义”在MATLAB里的标准做法。
表table,长得像Excel表格:
t = table([1;2;3], {'张三';'李四';'王五'}, 'VariableNames', {'ID', '姓名'});实际项目中 table 的威力很大,列可以有不同的数据类型,还能直接用列名访问t.ID,做数据分析和机器学习的时候几乎天天用。零基础阶段你只需要知道有这个东西,后面数据处理篇再深入展开。
2.5 别忽略矩阵这个庞然大物
最后要说一个MATLAB的底层思维:在MATLAB里,标量是一个1×1的矩阵,向量是1×n或n×1的矩阵,多维数组是n维矩阵。矩阵是MATLAB的“一等公民”,你写a = 5,本质上获得了一个1×1的 double 矩阵。这也是MATLAB叫“矩阵实验室”的原因。所以这一篇里学的每一句赋值语法,拿到矩阵场景下依然成立,后面的数组与矩阵篇会接续展开。
3. 查看变量类型与类型转换:实操必备技能
3.1 怎么知道一个变量是什么类型?
写脚本的时候,经常要确认手里的变量到底是什么类型。我给新手推荐三个方法。
第一,看工作区窗口。MATLAB默认布局右上角有 Workspace 面板,里面列出每个变量名、值、大小、类型,点一下还能进一步查看数据内容。观察它比敲命令直观得多。
第二,用whos命令。在命令行输入whos,会输出当前所有变量的名称、大小、字节数和 class。whos double可以只看指定类型。
第三,用class函数判断单个变量的类型。class(a)会返回'double'、'char'这样的字符串。配合isa可以写条件判断:isa(a, 'double')返回 true 说明 a 是 double。
这一组技能非常重要。图像处理场景下,你从imread读进来一张图,十有八九是 uint8,但很多算法函数要求 double 参数,你如果不查类型直接传进去,要么报错要么结果不对。能自己判断类型,等于你有了排查问题的第一个放大镜。
3.2 类型转换:别硬转,先看范围
类型转换就是让变量在类型之间迁移。常用函数前面已经提到,double()、single()、int8()、uint8()等等。记住一个总原则:转换之前先估一下目标类型能不能装下当前的数据,特别是把浮点数转整数、把大范围的数转小范围类型的时候。
几个实战场景:
场景一,图像显示。imread读入的图是 uint8,直接做运算可能溢出,先转成 double 做计算,完成后再转回 uint8 显示:
img = imread('photo.jpg'); % 读入图片,uint8类型 img_d = double(img); % 转 double 做计算 img_p = img_d / 255; % 归一化到 0~1 imshow(uint8(img_p * 255)); % 显示前转回 uint8场景二,字符和数值互转。num2str(3.14)得到'3.14';str2double('3.14')得到3.14。注意str2double比str2num健壮得多,str2num内部会调用 eval,有安全风险,性能也差。我建议把str2num这个函数拉黑,字符串转数值一律用str2double。
场景三,逻辑类型转换。logical(0)为 false,logical(5)为 true。在 mask 操作里特别常用,图像处理后得到的数值数组转成逻辑索引,就能按像素条件批量赋值。
还有一件事要时刻记住:MATLAB在数值运算中会做自动类型提升。比如uint8(200) + double(100),uint8会先转成 double 再相加,结果300。但uint8(200) + uint8(100)呢?结果不是300,而是255——uint8运算后的结果还是 uint8,溢出后饱和到最大值255。这个坑我在做图像像素操作时踩过无数次,所以做加法前,先确认运算双方是什么类型。
3.3 数字显示格式:看不全不等于丢了精度
还有一个跟数值显示相关的命令,初学者容易误解:format。format long能显示更多小数位,format short恢复默认的4位小数,format bank显示为带两位小数的货币格式。
要注意format只改变显示,不改变存储在变量里的实际值。很多人看到显示5.0就以为自己算错了,其实数据精度一直在,只是显示器隐藏了而已。想看完整值,用format long或者直接disp(x)。
3.4 结合一下:一个完整的变量实操小练习
纸上谈兵结束,来做一个综合小练习,把变量、类型、转换串一遍。在命令行依次输入:
a = 200; b = uint8(100); c = a + b; d = b + b; whos第一行和第二行创建了一个 double 变量a和一个 uint8 变量b。第三行,a + b时 b 自动转为 double,所以c是 double 类型的300。第四行,两个 uint8 相加,结果还是 uint8,溢出饱和到255。用whos看到的四行信息,清清楚楚展示出每个变量的类型差异。
你可以试着把b改成uint8(255),b + b的结果还是255,因为饱和了;改成uint8(127),结果就是254,没有溢出。通过这种小实验,你对“类型决定行为”会有非常直观的感受。
4. 新手最容易踩的坑(附解决方案)
4.1 变量名覆盖内置函数名
这是MATLAB特有的坑。sum、mean、max这些名字既是内置函数,也完全可以拿它们当变量名用,但——你一旦定义了sum = 100,那整个工作区里sum就变成变量了,你后面想调用sum()函数求和,MATLAB只会一脸懵。
解决办法:命名习惯上不要用常用函数名当变量名。真不小心踩了,清除变量clear sum就能恢复函数功能。这个坑的可怕之处在于它不报错,只会在运行结果上出问题,排查起来很费时间。
4.2 大小写敏感
前面说过MATLAB区分大小写。ImageData和imagedata是完全不同的两个变量。建议是从一开始就固定命名风格,统一用驼峰命名法,所有变量都按你定的风格来,大小写问题自然消失。
4.3 单引号双引号混用
char 和 string 混在一起赋给同一个变量,类型会在两个之间来回横跳,代码里就会出现“点击运行成功,换行报错”的诡异局面。解决办法在2.3节已经说了,确定一套风格,不要来回切换。
4.4 忘记加分号导致输出刷屏
严格来说这不是报错,但会让你的命令行窗口变成一坨乱麻。我的习惯是,赋值语句一律加分号,只有明确想看的语句才不加。
4.5 undefined function or variable:变量真的没定义
新手最常见报错是“未定义变量”。原因通常就几种:变量名拼错了、大小写对不上、脚本里的执行顺序还没运行到赋值那行、或者你 clear 掉了。排查思路:先用whos看看到底有没有这个变量,再看看是不是大小写问题。
4.6 cell花括号和圆括号的误用
再强调一次,c(1)取的是元胞,c{1}取的是内容。如果从c(1)里取出来的东西是 cell 类型,你拿去做数值运算就会报错。口诀:圆括号取包裹,花括号拆包裹。
4.7 运行前忘记清理旧变量
在脚本顶部修修改改,每次运行都基于上一次留下的一堆变量。最典型的情况是:你定义了一个循环变量 i,上一次运行到100,这次新代码里逻辑没覆盖到就出了问题。处理办法:脚本开头用clear;确保环境干净。注意前面我建议不要随时clear all,但脚本开头做一次全局清理是合理的。
5. 抛开语法,给零基础的你几个习惯建议
学变量和数据类型,真正难的永远不是语法,而是习惯。我见过太多新手把代码写成一坨无法维护的变量海,所以最后分享几个自己反复吃亏换来的习惯。
第一,给变量起名之前先想想这个变量未来会被谁读。不是只有别人会读,三个月后的你也是“别人”。imageData、maxValue这种名字,永远比x、y、a、b好。就算多敲几个字符,换来的读代码效率提升是值得的。
第二,每写完一段脚本,用whos看一遍变量清单,发现没用的变量及时 clear 掉。这能帮助你保持一个干净清爽的工作区,出问题时定位更快。
第三,变量赋值和类型转换尽量逐行写、逐行跑,不要一次性写完一大坨再回来看。零基础阶段,MATLAB的反馈速度就是你的学习速度。命令行窗口不像其他语言那样需要编译整个项目,你敲一句它执行一句,这是MATLAB对新手最大的友好之一。
第四,遇到类型转换相关的奇怪结果,先打印class(input)和class(output),八成问题出在类型匹配上。这个技巧我到现在还在用。
我记得自己当年第一次接触MATLAB,就是在网上找了一段图像处理的代码,复制粘贴运行报错无数,后来实在没办法,从头把变量和数据类型啃明白,才发现原来一大半报错都是类型问题引起的。那一瞬间有种顿悟感:不是代码难,是基础没打牢。所以这一篇虽然看起来啰嗦,但确实是你后面写任何MATLAB代码的根。把变量和数据类型这两块吃透,再往下的数组、矩阵、循环、条件判断,都会顺畅很多。