it 网站模板,团建活动策划,搜索引擎推广的网络营销渠道,自己做网站地址C初学者指南-4.诊断—基础知识#xff1a;警告和测试 文章目录 C初学者指南-4.诊断---基础知识#xff1a;警告和测试1. 术语和技术记住#xff1a;使用专用类型#xff01; 2.编译器警告Gcc/CLang 编译器选项MS Visual Studio 编译器选项 3.断言运行时断言静态断言#x…C初学者指南-4.诊断—基础知识警告和测试 文章目录 C初学者指南-4.诊断---基础知识警告和测试1. 术语和技术记住使用专用类型 2.编译器警告Gcc/CLang 编译器选项MS Visual Studio 编译器选项 3.断言运行时断言静态断言C11 4.测试指南doctest 简单测试案例doctest 子用例不要直接使用 cin/cout/cerr 1. 术语和技术
Warnings 警告编译器消息提示潜在的运行时行为问题的陷阱见下文Assertions 断言用于比较和报告表达式的预期值和实际值的语句见下文Testing 测试比较程序部分或整体的实际行为和预期行为见下文Code Coverage 代码覆盖率实际执行或测试的代码量gcov…Static Analysis 静态分析通过分析源代码发现潜在的运行时问题比如未定义的行为ASAN UBSAN…Dynamic Analysis 动态分析通过运行实际程序来发现潜在问题比如内存泄漏valgrind…Debugging 调试在运行时单步执行代码并检查内存中值(接下来)Profiling 分析找出每个函数/循环/代码块对总运行时间、内存消耗的影响有多大Micro Benchmarking 微基准测试衡量单个函数或一组语句/调用运行时间的小测试而不是整个程序运行
记住使用专用类型
限制输入参数值确保中间结果的有效性保证返回值的有效性
目标编译器作为正确性检查工具-如果编译成功那就应该是正确的。
// 输入保证角度以弧度为单位
Square make_rotated (Square const, Radians angle);
// 输入保证仅有效数量例如 0
Gadget duplicate (Gadget const original, Quantity times);
// 结果保证向量已经归一化
UnitVector3d dominant_direction (WindField const);
// 避免混淆使用一个好的单位库
si::kg mass (EllipsoidShell const, si::g_cm3 density);
…2.编译器警告
编译器错误 程序不可编译 编译器警告 程序可编译但有一段有问题的代码可能会导致运行时错误
Gcc/CLang 编译器选项
重要
-Wall强烈推荐。你应该始终至少使用这个。它并不完全启用所有警告而是启用那些最重要的不会产生太多误报的警告。-Wextra启用比 -Wall 还多的警告。强烈推荐-Wpedantic强烈推荐。需按照严格的ISO C发出所有警告拒绝编译器特定的扩展-Wshadow强烈建议。当变量或类型声明相互遮蔽时发出警告-Werror将所有警告视为错误⇒任何警告都将终止编译-fsanitizeundefined,address启用未定义行为检测器和地址检测器后文将更详细介绍
推荐的编译选项生产级别 -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror -fsanitizeundefined,address -Wfloat-equal -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wformat2 -Wimport -Winvalid-pch -Wlogical-op -Wmissing-declarations -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wnested-externs -Wpacked -Wpointer-arith -Wredundant-decls -Wstack-protector -Wstrict-null-sentinel -Wswitch-enum -Wundef -Wwrite-strings
高性能/低内存/安全 注这里的吵和噪音是指有很多警告的意思 可能会很吵 -Wdisabled-optimization -Wpadded -Wsign-conversion -Wsign-promo -Wstrict-aliasing2 -Wstrict-overflow5 -Wunused -Wunused-parameter
MS Visual Studio 编译器选项
/W11 级严重警告/W22 级重大警告/W33 级生产级别警告。 您应该始终至少使用这个。 也是较新的 Visual Studio 项目的默认值。/W4强烈推荐特别是对于新项目。 并没有真正启用所有警告而是启用最多的警告 重要的不会产生太多误报噪音。/Wall启用比 4 级更多的警告可能有点太吵了/WX将所有警告视为错误⇒任何警告都会终止编译
3.断言
运行时断言
#include cassert
assert(bool_expression);如果表达式 产生 false则中止程序 用例
在运行时检查预期值/条件验证先决条件输入值验证不变量例如中间状态/结果验证后置条件输出/返回值
应在发布版本中停用运行时断言 以避免任何性能影响。
#include cassert
double sqrt (double x) {assert( x 0 );…
}
double r sqrt(-2.3);$ g … -o runtest test.cpp
$ ./runtest
runtest: test.cpp:3: void sqrt(double): Assertion x 0 failed.
Aborted逗号必须用括号保护 assert 是一个预处理器宏稍后会详细介绍 否则逗号将被解释为宏参数分隔符
assert( min(1,2) 1 ); // ERROR
assert((min(1,2) 1)); // OK断言信息 可以使用自定义宏添加没有标准方法
#define assertmsg(expr, msg) assert(((void)msg, expr))
assertmsg(122, 1 plus 1 must be 2);不激活 – g/clang 通过定义预处理器宏 NDEBUG 来停用断言 例如使用编译器开关
g -DNDEBUG …不激活 – MS Visual Studio 断言已被明确激活
如果预处理宏 _DEBUG 被定义例如通过编译器开关 /D_DEBUG如果提供了编译器开关/MDd
如果在项目设置中或使用编译器开关/DNDEBUG定义了预处理宏NDEBUG那么断言会被明确地禁用。
静态断言C11
static_assert(bool_constexpr, message);
static_assert(bool_constexpr); (C17)如果编译时常量表达式产生 false则中止编译。
…
using index_t int;
index_t constexpr DIMS 1; // oops
void foo () { static_assert(DIMS 1, DIMS must be at least 2);…
}
index_t bar (…) {static_assert(std::numeric_limitsindex_t::is_integer std::numeric_limitsindex_t::is_signed, index type must be a signed integer);…
}$ g … test.cpp
test.cpp: In function void foo():
test.cpp:87:19: error: static assertion failed: DIMS must be at least 287 | static_assert(DIMS 1, DIMS must be at least 2);| ~~^~~4.测试
指南
使用断言 检查类型无法表达或保证的期望和假设
仅在运行时可用的期望值前提条件输入值不变量例如中间状态/结果后置条件输出/返回值
在发布版本中应该关闭运行时断言以避免任何性能影响。
编写测试 一旦确定了函数或类型的基本用途和接口。
更快的开发减少耗时的日志记录和调试会话需求。更容易的性能调优可以持续检查是否仍然正确。文档期望/假设被写在代码中。
使用测试框架 更方便更少出错预定义检查、设置设施、测试运行器等。 初学者 / 较小的项目 doctest
非常简洁和自我说明的风格轻松设置只需包含一个标题非常快的编译
大型项目Catch2
与doctest相同的基本理念doctest是模仿Catch设计的。用不同数值执行相同测试的数值生成器。使用计时器进行微基准测试取平均值等。编译速度较慢设置起来比doctest稍微复杂一些。
doctest 简单测试案例
// 摘自文档测试教程:
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include doctest.h
int factorial (int n) {if (n 1) return n;return factorial(n-1) * n;
}
TEST_CASE(testing factorial) {CHECK(factorial(0) 1);CHECK(factorial(1) 1);CHECK(factorial(2) 2);CHECK(factorial(3) 6);CHECK(factorial(10) 3628800);
}$ g … -o runtest test.cpp
$ ./runtest
test.cpp(7) FAILED!
CHECK( factorial(0) 1 )
with expansion:
CHECK( 0 1 )测试失败因为factorial的实现无法正确处理 n 0 的情况。 运行此示例
doctest 子用例
// 摘自文档测试教程:
TEST_CASE(vectors can be sized and resized) {std::vectorint v(5);REQUIRE(v.size() 5);REQUIRE(v.capacity() 5);SUBCASE(push_back increases the size) {v.push_back(1);CHECK(v.size() 6);CHECK(v.capacity() 6);}SUBCASE(reserve increases the capacity) {v.reserve(6);CHECK(v.size() 5);CHECK(v.capacity() 6);}
}对于每个子用例测试用例都是从头开始执行的。 当执行每个子用例时我们知道vector大小为 5容量至少为 5。 我们在顶层通过 REQUIRE 强制执行这些要求。
如果 CHECK 失败测试被标记为失败但执行会继续进行。如果 REQUIRE 失败执行停止。 运行此示例
不要直接使用 cin/cout/cerr
直接使用全局 I/O 流使得函数或类型难以测试
void bad_log (State const s) { std::cout … }在函数中通过引用传递流
struct State { std::string msg; … };void log (std::ostream os, State const s) { os s.msg; }TEST_CASE(State Log) {State s {expected};std::ostringstream oss;log(oss, s);CHECK(oss.str() expected);
}运行上面示例
类范围存储流指针 但是请尝试编写与流或任何其他特定I/O方法无关的类型。
class Logger {std::ostream* os_;int count_;
public:explicitLogger (std::ostream* os): os_{os}, count_{0} {}…bool add (std::string_view msg) {if (!os_) return false;*os_ count_ : msg \n;count_;return true;}
};TEST_CASE(Logging) {std::ostringstream oss;Logger log {oss};log.add(message);CHECK(oss.str() 0: message\n);
}运行上面示例
附上原文链接 如果文章对您有用请随手点个赞谢谢^_^