Program SMaRT:
Performance(高效)、Portable(可移植性)、Simple(简洁)、Maintainable(可维护)、Robustness(可靠)、Testable(可测试)
编程规范
- 模块
- 独立完成指定功能:接口(a.h),实现(a.c),数据(a_data)
- 函数
- 语句
头文件
- 编译过程:
- 预处理:头文件插入
- 编译优化
- 汇编
- 链接
- 头文件应当职责单一:头文件插入-->查找、打开、读取、拷贝
- 禁止包含用不到的头文件
- 头文件中适合放置接口声明,不适合放实现
- 头文件中禁止定义变量
- 头文件应向稳定的方向包含:头文件的变化,会引起包含该头文件的模块重新编译
- 每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口
- 只能通过包含头文件的方式使用其他模块提供的接口,禁止在.c中通过extern的方式使用外部函数接口,变量
- 禁止头文件循环依赖
- 总是编写内部的“ifndef”保护符
- 头文件应当自包含
- 禁止在extern "C"中包含头文件
函数
- 降低复杂度,易于思考
- 便于协同开发
- 代码复用,减少代码量
好代码:简单易懂
函数编程规范
- 圈复杂度小于7
- 函数要短小(小于50)
- 嵌套层数小于5
- 职责单一
- 单一抽象层次
- 参数不超过3个
- 避免重复:避免无意识的数据重复
- 避免重入
- 不要返回局部变量地址
语句
- 命名
- 注释
- 格式
- 效率
- 安全
数据区:
- 常量区,全局变量区,静态变量区(编译时进行分配)
- 堆,栈(执行时进行分配)
代码区:存放指令
堆管理
typedef struct t_heap-nd{
uint16 size;
void* data;
t_heap_nd* next;
}heap_node;
char *strchr(const char *s,char c),可以查找字符串s中首次出现字符c的位置,返回字符串s中字符c第一次出现的地址(指针)
申请粒度不宜过细,过细的建立使用栈空间(建议大于8字节):因为管理空间需要6个字节
栈–有限的
#define MAX_NUM 0x400000 (4M)
- 不要返回或传递栈变量
- 进程间通信不要传递栈变量.
类型强制转换
- 无符号和有符号互转
int x = -5;
unsigned int nx = (unsigned int)x;
x = (int)nx;
- 截断
- 整数溢出
- 字节对齐
- 字节序(大端和小端)
- 字符串
- strcat:将两个char类型连接;strcat(d, s):结果放在d中;d和s所指内存区域不可以重叠且d必须有足够的空间来容纳s的字符串
- strcpy:C的标准库函数,strcpy(char* dest, const char* src):把从src地址开始且含有'\0'结束符的字符串复制到以dest开始的地址空间;src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串
- strncpy:库函数之一;char* strncpy(char* dest, const char* src, int n);以src地址开始的前n个字节复制到dest所指的数组中;
- sprintf:字符串格式化命令,把格式化的数据写入某个字符串中。sprintf是个变参函数,使用sprintf对于写入buffer的字符数没有限制,这就存在buffer溢出的可能性;int sprintf(char* buffer, const char*format, [argument]...);
- memset:void * memset(void* s, int ch, size_t n);将s中当前位置后面的n个字节用ch替换并返回s
- memcpy:void* memcpy(void* dest, const void* src, size_t n);从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置。
char name[6] = "test";
printf("0x%X", name + 1); 指针运算的步长是指针指向类型的长度
printf("0x%X", &name + 1); 对数组取地址得到的是指向数组的指针,所以相当于加6
低级错误案例–修改函数形参
- 函数内部不能改变指针所指向的地址
- 函数参数所在存储单元的直接修改不会作用到函数之外(函数调用完立即释放)
- 对参数存储单元中存放的地址指向的存储空间的修改,则会在函数之外起作用
低级错误案例–返回局部变量的地址
低级错误案例 –强制类型转换和字节序
- 内存越界
- 当目的结构的空间大于源结构的空间时,重点关注内存访问超过源结构范围的情形,可能越界
- 当目的结构的空间小于源结构的空间时,关注对目的结构赋值不能完全覆盖源结构范围的情形,需要考虑字节序
- 当把某些变量或数组或结构强制转换成另一个结构体时,需要考虑结构体的字节对齐问题
- 与结构体之间的强制类型转换相比,基本数据结构类型的强制类型转换更容易出现上面描述的情况
- 不要将字符串常量强制转化为字符指针,以免导致改写只读数据段的段错误
低级错误案例 –变量溢出
- 循环变量使用时需要注意:
- 使用循环变量要确保循环变量的取值范围要大于循环范围的限制常量或变量
- 使用循环变量,不能使用UC类型,最好使用US或UL,避免循环边界值大于255形成死循环
低级错误案例 –堆栈溢出
定义变量和结构体时:
- 是否有超过1K的局部变量或者数组;或者局部变量总和大于4K时要有例外说明
- 注意结构体定义中有可能嵌套另外结构体
低级错误案例 –字符串的使用
sprintf
- 使用字符串数组:
- 时刻注意字符串的‘\0’结束符,以及字符串长度(strlen)和大小(sizeof)的问题
- 尽量使用strcpy_s, sprintf_s等有长度校验的安全函数
- 通过索引读取字符串时,一定要判断索引的正确性
IPD及敏捷
IPD:集成产品开发
- 技术环节:6个TR点
- TR1:弄清楚原始需求
- TR2:根据原始需求分解出产品的设计需求与设计规格
- TR3:对每一个规格进行详细的设计
- TR4和TR4A:具体实施(研发)
- TR5:关注产品规格是否完备,功能是否齐全,是否达到验收标准
- TR6:试验
- 管理环节:业务评审(4个),关注产品的市场定位及盈利情况
- 对应技术环节的六个点:概念,计划,开发,验证,发布,生命周期
- 发布点
- GA点:项目进入维护周期
敏捷:软件开发模式
敏捷宣言:我们正在通过亲身实践以及帮助他人实践,揭示更好的软件开发方法
- 个体和交互胜过过程和工具
- 可以工作的软件胜过面面俱到的文档
- 客户合作胜过合同谈判
- 响应变化胜过遵循计划
统一认识敏捷和敏捷理念
三个层次:
- 理念(敏捷核心思想):value、team、adapting
- 聚焦客户价值(value),消除浪费
- 激发团队(Team)潜能,加强协作
- 不断调整以适应(Adapting)变化
- 优秀实践(敏捷的经验积累):站立会议和结对编程、持续集成和测试驱动开发、重构和迭代
- 具体应用(能结合自身灵活应用才是真正的敏捷)
测试基础
测试的定义:测试是一个包含计划、准备和测量活动的过程。其目的是确认被测系统的特性,并指出需求和实现之间的差异
测试的目的:发现缺陷,增强对质量的信心,为决策者提供信息,预防缺陷
测试分类:
- 静态测试和动态测试
- 单元测试,系统测试,集成测试和验收测试
- 单元:C中的函数和C++的类,功能单一且独立
- 集成测试:单元、组件之间的接口进行测试
- 系统测试:关注的是在开发项目或程序中定义的一个完整的系统或产品的行为
- 验收测试:通常是使用系统的用户或客户来进行,同时利益的相关者也可能参与其中
- 黑盒测试和白盒测试
- 黑盒测试:把被测对象看作一个黑盒,测试软件产品的功能,而不需关注软件产品的内部结构和处理过程:等价类划分;边界值分析;决策表分析
- 白盒测试:根据被测程序的内部结构设计测试用例的一种测试方法:语句覆盖;判定覆盖;条件覆盖;路径覆盖
- 静态测试与动态测试
- 静态测试:通过手工检查(评审)或自动化分析(静态分析工具)的方式对代码或其他项目文档进行检查:代码检查工具;走读;检视
- 动态测试:通过运行被测程序,检查运行结果与预期结果的差异,并分析运行效率和健壮性等性能,组成:构造测试实例,执行程序,分析程序的输出结果
测试依据:业务要求,合同要求,法律标准,行业标准
Who:测试不仅仅是测试人员的事情,测试是研发团队所有人的职责
When:启动:测试越早越好
何时停止测试:需要从多个维度进行综合评估:成本;时间;被发现的缺陷数量;覆盖;法律要求必要的条件;合同要求;行业标准
- 当达到了必要的信心级别,风险可以接受的时候停止测试
- 当发现缺陷的代价高于缺陷发生引起的代价时停止测试
- 当达到测试完成标准(退出或成功)时停止测试
LIT(Low level Test)和HLT(High level Test)高层测试共同编织一张系统质量保护网
How:基本测试标准过程:计划;控制;分析和设计;评估和结束
How better:制定测试策略是测试的关键活动之一
- 为什么需要基于风险的测试(RBT)?穷尽测试就是进行完全(各种输入和前提条件的组合)的测试,对资源的需求巨大
- 产品的风险级别由失效概率(风险发生的可能性)和失效影响(风险发生后产生的影响)两个方面来决定
- 可运行优先,风险优先的执行策略
- 风险高的特性,测试需要多投入
测试基本原则
- 测试可以显示缺陷的存在
- 穷尽测试是不可能的:应使用风险分析和优先级来聚焦测试投入
- 测试尽早介入
- 测试集群性:版本发布前进行的测试与软件实际运行中出现的失效,所发现的大部分缺陷是由于少数软件模块引起的
- 杀虫剂悖论:测试用例需要经常性的评审和修改,同时需要不断增加新的不同的测试用例来测试软件或系统的不同部分,从而发现潜在的更多的缺陷
- 测试活动依赖于测试上下文:测试与其所处的项目环境相关;针对不同的测试内容,进行的测试活动也是不同的
- “Absence-of-errors” 谬误:系统的发布,不能取决于是否存在缺陷,而是取决于是否满足客户的需求和期望
华为测试潜规则
- 可以规划软件中缺陷的数量
- 测试周期是可以压缩的
- 测试在前期的工作只能是学习
- 缺陷都应该用“三板斧”来发现
- 姜是老的辣,用例是陈的香
- 任何一个测试项目都是可以复制的
- 超出设计规格的缺陷都不是缺陷
- 自动化率决定测试能力的高低
- 可以忽略测试人员对流程和指标的任何疑问
- 任何情况下,TR点时的度量数据都是满足要求的
- 规格是测试出来的,而不是设计出来的
测试的基本心理学
- 测试需要一定的天赋:具有很强的好奇心
- 测试是一项全身运动(专业的怀疑态度)
- 测试人员必备的六副眼镜:墨镜;遮羞镜;潜水镜;一套新眼镜;放大镜;防护镜(一双挑剔的眼睛,探究根底的精神)
- 好的测试人员都是沟通高手:建设性的态度;客观理性;换位思考
- 对常见的错误进行判断的经验
三个故事
- 向客户讲述产品故事:什么情况下可以工作?什么情况会失效?什么情况下可能失效?失效情况下会有哪些影响?–产品故事就是定性的产品报告
- 测试故事:关于测试如何完成的故事,就是我们是怎么配置、操作、观察和评估产品的。测试故事就是为了让客户理解和信任我们的产品故事。为产品故事提供依据和支持
- 测试怎么才能做得更好的故事:测试人员要记录测试的风险和成本,讨论那些问题降低了测试的速度,直接威胁到测试的质量,进而间接威胁产品、项目以及业务的质量?什么使测试更困难或者更缓慢,什么影响了产品的可测试性,测试的风险和成本如何
GTEST
- 一种UT(LLT)测试工具
- C/C++:HUTAF LLT 和GoogleTest
- Java:Junit
条件检查:
- EXPECT_* 失败时,用例继续往下执行
- ASSERT_*失败时,直接在当前函数中返回,当前函数中ASSERT_*后面的语句将不会执行
数值检查
字符串检查
显示断言
自定义错误信息
测试用例基本结构
测试套
全局环境准备
用例执行顺序
用例执行
用例调试