C++初学者指南-4.诊断---基础:警告和测试

C++初学者指南-4.诊断—基础知识:警告和测试

文章目录

  • C++初学者指南-4.诊断---基础知识:警告和测试
    • 1. 术语和技术
      • 记住:使用专用类型!
    • 2.编译器警告
      • Gcc/CLang 编译器选项
      • MS Visual Studio 编译器选项
    • 3.断言
      • 运行时断言
      • 静态断言(C++11)
    • 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将所有警告视为错误⇒任何警告都将终止编译
-fsanitize=undefined,address启用未定义行为检测器和地址检测器(后文将更详细介绍)

推荐的编译选项(生产级别)
-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror -fsanitize=undefined,address -Wfloat-equal -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wformat=2 -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-aliasing=2 -Wstrict-overflow=5 -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(1+2==2, "1 plus 1 must be 2");

(不)激活 – g++/clang
通过定义预处理器宏 NDEBUG 来停用断言, 例如,使用编译器开关:

g++ -DNDEBUG …

(不)激活 – MS Visual Studio
断言已被明确激活

  • 如果预处理宏 _DEBUG 被定义,例如,通过编译器开关 /D_DEBUG
  • 如果提供了编译器开关/MDd

如果在项目设置中或使用编译器开关/DNDEBUG定义了预处理宏NDEBUG,那么断言会被明确地禁用。

静态断言(C++11)

static_assert(bool_constexpr, "message");
static_assert(bool_constexpr);  (C++17)

如果编译时常量表达式产生 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_limits<index_t>::is_integer &&std::numeric_limits<index_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::vector<int> 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");
}

运行上面示例

附上原文链接
如果文章对您有用,请随手点个赞,谢谢!^_^

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/41962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS查看系统日志的方法

1、command空格键打开搜索框&#xff0c;输入‘控制台’并打开 2、选择日志报告&#xff0c;根据日期打开自己需要的文件就可以

c++习题06-ljc的储蓄计划

目录 一&#xff0c;问题 二&#xff0c;思路 1&#xff0c;思路1 2&#xff0c;思路2 三&#xff0c;代码 1&#xff0c;思路1代码 2&#xff0c;思路2代码 四&#xff0c;用例输入输出 一&#xff0c;问题 二&#xff0c;思路 1&#xff0c;思路1 定义变量&#x…

统计是一门艺术(非参数假设检验)

1.定义 当总体分布未知&#xff0c;那么就需要一种与分布具体数学形式无关的统计推断方法&#xff0c;称为非参数方法 只能利用样本中的一般信息包括位置和次序关系等 稳健性强 2.符号检验 考虑问题&#xff1a; 小样本情况&#xff1a; 以概率为1/2的二项分布是对称的 两…

剖析DeFi交易产品之UniswapV3:交易路由合约

本文首发于公众号&#xff1a;Keegan小钢 SwapRouter 合约封装了面向用户的交易接口&#xff0c;但不再像 UniswapV2Router 一样根据不同交易场景拆分为了那么多函数&#xff0c;UniswapV3 的 SwapRouter 核心就只有 4 个交易函数&#xff1a; exactInputSingle&#xff1a;指…

Java技术栈总结:kafka篇

一、# 基础知识 1、安装 部署一台ZooKeeper服务器&#xff1b;安装jdk&#xff1b;下载kafka安装包&#xff1b;上传安装包到kafka服务器上&#xff1a;/usr/local/kafka;解压缩压缩包&#xff1b;进入到config目录&#xff0c;修改server.properties配置信息&#xff1a; #…

Buuctf之SimpleRev做法

首先&#xff0c;查个壳&#xff0c;64bit&#xff0c;那就丢进ida64中进行反编译进来之后&#xff0c;我们进入main函数&#xff0c;发现里面没什么东西&#xff0c;那就shiftf12搜索字符串&#xff0c;找到关键字符串&#xff0c;双击进入然后再选中该字符串&#xff0c;ctrl…

Python爬取股票信息-并进行数据可视化分析,绘股票成交量柱状图

为了使用Python爬取股票信息并进行数据可视化分析&#xff0c;我们可以使用几个流行的库&#xff1a;requests 用于网络请求&#xff0c;pandas 用于数据处理&#xff0c;以及 matplotlib 或 seaborn 用于数据可视化。 步骤 1: 安装必要的库 首先&#xff0c;确保安装了以下P…

【C语言】指针(1):入门理解篇

目录 一、内存和地址 1.1内存 1.2 深入理解计算机编址 二、指针变量和地址 2.1 取地址操作符&#xff08;&&#xff09; 2.2 指针变量和解应用操作符 2.2.1 指针变量 2.2.2 解引用操作符 2.3指针变量的大小 三、指针变量类型的意义 3.1 指针的解引用 3.1指针-整数…

Micron近期发布了32Gb DDR5 DRAM

Micron Technology近期发布了一项内存技术的重大突破——一款32Gb DDR5 DRAM芯片&#xff0c;这项创新不仅将存储容量翻倍&#xff0c;还显著提升了针对人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;、高性能计算&#xff08;HPC&#xff09;以及数…

2024年最新运维面试题(附答案)

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 一&#xff0e;选择题 1.HTTP协议默认使用哪个端口…

科普文:构建可扩展的微服务架构设计方案

前言 微服务架构是一种新兴的软件架构风格&#xff0c;它将单个应用程序拆分成多个小的服务&#xff0c;每个服务都运行在自己的进程中&#xff0c;这些服务通过网络进行通信。这种架构的优势在于它可以提高应用程序的可扩展性、可维护性和可靠性。 在传统的应用程序架构中&…

高效管理个人日程,智慧校园行政办公全指南

在智慧校园的行政办公体系里&#xff0c;个人日程管理功能担当起协调与优化每位教职员工日常安排的角色&#xff0c;它像一位贴心的时间助理&#xff0c;确保工作与私人生活的和谐并进。这一功能设计得既直观又灵活&#xff0c;让使用者能以自己偏好的视角审视时间规划&#xf…

创新配置,秒级采集,火爆短视频评论抓取

快速采集评论数据的好处 快速采集评论数据是在当今数字信息时代的市场趋势分析和用户反馈分析中至关重要的环节。通过准确获取并分析大量用户评论&#xff0c;您将能够更好地了解消费者的需求、情感和偏好。集蜂云采集平台提供了一种简单配置的方法&#xff0c;使您能够快速采…

Deep Filtered Back Projection for CT Reconstruction

CT重建中的深度滤波反投影 论文链接&#xff1a;https://ieeexplore.ieee.org/document/10411896 项目链接&#xff1a; ABSTRACT 滤波反投影(FBP)是一种经典的计算机断层扫描(CT)重建解析算法&#xff0c;具有很高的计算效率。然而&#xff0c;用FBP重建的图像往往存在过多…

NATAPP内网穿透使用

1. natapp能干嘛 可以将本地的内网ip映射到外网上&#xff0c;远程访问该连接&#xff0c;实现外网展示网站。平时做的应用开发都只能在局域网本地访问&#xff0c;通过内网穿透&#xff0c;可以通过外网进行访问。 2. 注册用户 网址&#xff1a;https://natapp.cn/自行完成…

什么是 Elasticsearch 数据预热?

引言&#xff1a;在现代的信息检索和数据分析领域&#xff0c;Elasticsearch 已经成为一个广泛应用的分布式搜索和分析引擎。作为开源项目的一部分&#xff0c;Elasticsearch 提供了强大的实时搜索和分析能力&#xff0c;使得处理大规模数据变得更加高效和可靠。然而&#xff0…

Canary,三种优雅姿势绕过

Canary&#xff08;金丝雀&#xff09;&#xff0c;栈溢出保护 canary保护是防止栈溢出的一种措施&#xff0c;其在调用函数时&#xff0c;在栈帧的上方放入一个随机值 &#xff0c;绕过canary时首先需要泄漏这个随机值&#xff0c;然后再钩爪ROP链时将其作为垃圾数据写入&…

对接海康sdk-linux下复制jar包中resource目录的文件夹

背景 在集成海康sdk时,需要将一些组件放到项目中作为静态资源,并且海康的sdk初始化也需要加载这些静态资源,在windows下,使用一些File路径的方式是可以正确加载的,但是在linux上就会加载失败。 首先我是将海康的sdk组件放到resource下的,并且按照windows和linux设置了两…

轻松快速上手Thekey库,实现数据加密无忧

Thekey的概述&#xff1a; Thekey库是一个Python库,旨在简化数据加密、解密、签名和验证的过程。它提供了一套简洁易用的接口,用于处理各种加密任务,适合需要在应用程序中实现安全数据处理的开发人员. 安装Thekey库 pip install thekey使用Thekey库进行基本加密和解密操作的…

【笔记】TimEP Safety Mechanisms方法论

1.TimEPM Overview 三大监控方法: Alive Supervision 实时监督Logical Supervision 逻辑监督Deadline Supervision 限时监督相关模块框图: 相关模块调用框图: 每个MCU核开启内狗(1核1狗),内狗用于监控相应核的TASK超时,超时后软reset MCU内狗时钟需要独立于OS时钟,两…