第四章:测试语言基础(上)——变量与数据类型、运算符与表达式、控制流语句
各位同学,欢迎来到测试语言基础的第一部分。说实话,很多刚入行的工程师觉得语言基础太简单,不就是变量、循环、判断吗?但我在ATE测试这个行当里摸爬滚打这么多年,可以负责任地告诉你:基础不牢,地动山摇。测试程序里一个变量类型用错,可能让你排查三天三夜。
4.1 变量与数据类型——给数据找个合适的“家”
变量是什么?说白了,就是内存里的一块空间,用来存数据。但ATE测试对数据类型的要求,比普通软件开发要严格得多。为什么?因为我们要跟硬件寄存器、测试仪器打交道,数据位宽、符号、精度,差一点都不行。
4.1.1 基本数据类型
我习惯把测试语言的数据类型分成三类:
- 整数型:int、short、long。ATE里最常用的是int,32位有符号。但注意,有些测试机台的编译器int是16位的,踩过坑的人都知道。
- 浮点型:float、double。测电压、电流、频率这些模拟量,必须用浮点。我个人建议能用double就别用float,精度差一个数量级,关键时刻会坑你。
- 字符型:char、string。存测试项名称、bin号、日志信息。
重点提醒:ATE测试程序里,int和double是绝对的主力。我见过有人用float存电压值,结果因为精度不够,导致良率误判。嗯,那场面,挺尴尬的。
4.1.2 变量声明与初始化
变量声明这件事,看起来简单,但有个原则我必须强调:先声明,后使用。而且,一定要初始化!
// 正确的做法
int test_count = 0;
double vdd_voltage = 0.0;
char* test_name = "OPEN_SHORT";
// 我曾经见过这样的代码
int result;
// ... 中间隔了200行
if (result == 1) { // result根本没赋值!
// 逻辑永远不会执行
}
避坑指南:我曾经在调试一个量产程序时,发现某个测试项偶尔会误判。查了两天,最后发现是一个局部变量没初始化,每次调用时它的值是随机的。从那以后,我声明变量必初始化,成了强迫症。
4.1.3 作用域与生命周期
变量不是在哪都能用的。全局变量、局部变量、静态变量,它们的“活动范围”不一样。
| 类型 | 作用域 | 生命周期 | ATE场景举例 |
|---|---|---|---|
| 全局变量 | 整个文件 | 程序运行期间 | 测试系统配置参数 |
| 局部变量 | 函数内部 | 函数执行期间 | 循环计数器 |
| 静态变量 | 函数内部 | 程序运行期间 | 测试项调用次数统计 |
你想想看,如果把一个循环计数器声明成全局变量,多线程调用时会发生什么?数据错乱!所以,能用局部就别用全局,这是铁律。
4.2 运算符与表达式——让数据“动”起来
变量是食材,运算符就是厨具。怎么把原始数据加工成我们想要的测试结果?靠的就是运算符。
4.2.1 算术运算符
加减乘除、取模,这些大家都熟。但ATE测试里有个特殊场景:除法要小心。
int a = 5;
int b = 2;
double c = a / b; // 结果是2.0,不是2.5!
double d = (double)a / b; // 这才是2.5
为什么会这样?因为整数除法会截断小数部分。我在项目中遇到过有人用整数除法算频率,结果所有测试值都偏小,整批货差点报废。
4.2.2 关系运算符与逻辑运算符
判断测试通过还是失败,全靠它们。
- 关系运算符:>、<、>=、<=、==、!=
- 逻辑运算符:&&、||、!
个人经验:判断浮点数相等,千万别用 ==!因为浮点精度问题,0.1 + 0.2 可能不等于 0.3。我习惯这样写:fabs(a - b) < 0.0001。记住这个写法,能救你命。
4.2.3 位运算符
这是ATE测试的“杀手锏”。寄存器操作、标志位判断、数据打包解包,全得靠它。
// 读取寄存器bit3的值
unsigned int reg_value = 0xA5; // 1010 0101
int bit3 = (reg_value >> 3) & 0x01; // 结果是0
// 设置bit5为1
reg_value |= (1 << 5); // 变成1100 0101
// 清除bit2
reg_value &= ~(1 << 2); // 变成1100 0001
嗯,这里要注意:位运算一定要用无符号类型。有符号数的右移是算术右移,会补符号位,结果跟你想象的可能完全不一样。
4.3 控制流语句——让程序有“脑子”
没有控制流的程序,就是一条道走到黑。测试程序需要判断、需要循环、需要根据条件选择不同的测试路径。
4.3.1 if-else 判断
最基础的条件判断。但ATE测试里,if-else 用得特别多,因为每个测试项都要判断是否在规格范围内。
double measured_voltage = 1.25;
double vmin = 1.20;
double vmax = 1.30;
if (measured_voltage >= vmin && measured_voltage <= vmax) {
// 测试通过
printf("PASS: Voltage = %.3fV\n", measured_voltage);
} else {
// 测试失败
printf("FAIL: Voltage = %.3fV (out of range)\n", measured_voltage);
}
避坑指南:我曾经在写多条件判断时,把 && 写成了 &。结果逻辑完全错了,一批良率90%的货被误判成50%。从那以后,我写条件判断都会多看一眼运算符。
4.3.2 switch-case 多分支
当判断条件很多时,switch-case 比 if-else 更清晰。比如根据测试bin号执行不同操作:
int bin = get_test_bin();
switch (bin) {
case 1:
// 良品处理
break;
case 5:
case 6:
// 参数偏差,需要复测
break;
case 10:
// 致命失效,直接报废
break;
default:
// 未知bin,报警
break;
}
注意,别忘了 break!忘了的话,程序会“穿透”执行下一个case。我刚开始写程序时就犯过这个错,调试了一下午才发现。
4.3.3 for 循环
ATE测试里,for循环是绝对的主力。遍历所有测试项、重复测量多次取平均、扫描多个电压点……全得靠它。
// 测量5次取平均值
double sum = 0.0;
int i;
for (i = 0; i < 5; i++) {
sum += measure_voltage();
}
double average = sum / 5.0;
我个人习惯把循环变量 i 声明在循环外面。为什么?因为有些编译器在循环结束后,i 的值还能用,方便调试。
4.3.4 while 循环
当循环次数不确定时,用 while。比如等待测试仪器准备好:
int timeout = 1000;
while (!is_instrument_ready() && timeout > 0) {
delay(1); // 等待1ms
timeout--;
}
if (timeout == 0) {
printf("ERROR: Instrument timeout!\n");
}
重要提醒:while循环一定要有退出条件,否则就是死循环。我在调试一个老化测试程序时,忘了加超时机制,结果程序卡在等待仪器响应上,整个测试批次都停了。嗯,那次被领导骂得不轻。
4.3.5 break 与 continue
这两个是循环的“控制杆”。
- break:跳出整个循环
- continue:跳过本次循环,进入下一次
// 找到第一个失败的测试项就停止
for (i = 0; i < total_tests; i++) {
if (run_test(i) == FAIL) {
printf("First failure at test %d\n", i);
break; // 跳出循环
}
}
// 跳过不需要测试的项目
for (i = 0; i < total_tests; i++) {
if (is_test_skipped(i)) {
continue; // 跳过本次
}
run_test(i);
}
4.4 综合实战:一个简单的测试流程
好了,理论讲完了。咱们来写一个完整的测试片段,把今天学的都用上。
// 变量声明与初始化
int test_count = 0;
int pass_count = 0;
int fail_count = 0;
double voltage_reading = 0.0;
const double VDD_MIN = 1.10;
const double VDD_MAX = 1.30;
// 测试主循环
for (test_count = 0; test_count < 100; test_count++) {
// 测量电压
voltage_reading = measure_vdd();
// 判断是否在范围内
if (voltage_reading >= VDD_MIN && voltage_reading <= VDD_MAX) {
pass_count++;
printf("Test %d: PASS (%.3fV)\n", test_count, voltage_reading);
} else {
fail_count++;
printf("Test %d: FAIL (%.3fV)\n", test_count, voltage_reading);
// 连续失败3次,终止测试
if (fail_count >= 3) {
printf("Too many failures, aborting...\n");
break;
}
}
}
// 输出统计结果
printf("Total: %d, Pass: %d, Fail: %d\n", test_count, pass_count, fail_count);
这段代码虽然简单,但包含了变量声明、常量定义、for循环、if-else判断、break控制。你想想看,如果把这个逻辑扩展到几百个测试项、几十个参数,是不是就能撑起一个完整的量产测试程序了?
最后说一句:语言基础就像盖房子的地基。我见过太多工程师,急着学高级特性、学仪器控制,结果连个变量作用域都搞不清楚,写出来的程序bug百出。把今天的内容消化透,后面的课程你会学得轻松很多。