【C++20】编译期检测所有未定义行为undefined behavior和内存泄漏(不借助编译选项以及任何外部工具)

本文主要是介绍【C++20】编译期检测所有未定义行为undefined behavior和内存泄漏(不借助编译选项以及任何外部工具),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、未定义行为Undefined Behavior(UB)
    • 1.返回一个未初始化的局部变量的值
    • 2.数组越界访问
    • 3.有符号数的常量表达式溢出
    • 4.new与delete
    • 5.vector
    • 6.空指针解引用
  • 参考

一、未定义行为Undefined Behavior(UB)

在C++中,未定义行为(Undefined Behavior)指的是程序的行为没有定义、不可预测或不符合C++标准的情况。当程序中存在未定义行为时,编译器和运行时环境不会对其进行任何保证,可能会导致程序产生意外的结果。

以下是一些常见的导致未定义行为的情况:

  • 访问未初始化的变量:如果使用未初始化的变量,其值是不确定的,可能包含任意的垃圾值。

  • 数组越界访问:当访问数组时,如果超出了数组的有效索引范围,将导致未定义行为。

  • 空指针解引用:当将空指针用作指针解引用,即访问其指向的内存区域时,将导致未定义行为。

  • 除以零:在C++中,除以零是一种未定义行为,可能导致程序崩溃或产生无效的结果。

  • 使用已释放的内存:如果使用已释放的内存,或者使用指向已释放内存的指针,将导致未定义行为。

  • 栈溢出:当递归调用或者使用过多的局部变量导致栈空间耗尽时,将导致未定义行为。

  • 多个线程之间的竞争条件:如果多个线程同时访问并修改共享数据而没有适当的同步机制,可能会导致未定义行为。

编译器使用x86_64 gcc13.2
C++版本:-std=c++20

1.返回一个未初始化的局部变量的值

UB写法:

#include <cstdio>int func()
{int i;return i;
}int main()
{int i = func();printf("%d\n",i);return 0;
}

编译及运行结果:

Program returned: 0
Program stdout
0

使用编译期constexpr检测UB:

#include <cstdio>constexpr int func()
{int i;return i;
}int main()
{constexpr int i = func();printf("%d\n",i);return 0;
}

编译及运行结果:

<source>: In function 'constexpr int func()':
<source>:6:9: error: uninitialized variable 'i' in 'constexpr' function6 |     int i;|         ^
<source>: In function 'int main()':
<source>:12:27: error: 'constexpr int func()' called in a constant expression12 |     constexpr int i = func();|                       ~~~~^~

通过constexpr 进行UB检测:

#include <cstdio>constexpr int func(int i)
{if (i >= 0)return 0;}int main()
{constexpr int _1 = func(1);constexpr int _2 = func(-1);return 0;
}

编译及运行:

Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In function 'int main()':
<source>:14:28:   in 'constexpr' expansion of 'func(-1)'
<source>:14:31: error: 'constexpr' call flows off the end of the function14 |     constexpr int _2 = func(-1);|

编译期使用constexpr 检测UB 写法(关于移位的例子):

  • int类型的数据(4Bytes),最多只能移动31bit
#include <cstdio>constexpr int func(int i)
{return 1 << i;
}int main()
{constexpr int _1 = func(32);constexpr int _2 = func(-1);printf("%d\n",_1);return 0;
}

编译及运行:


<source>: In function 'int main()':
<source>:11:28:   in 'constexpr' expansion of 'func(32)'
<source>:6:14: error: right operand of shift expression '(1 << 32)' is greater than or equal to the precision 32 of the left operand [-fpermissive]6 |     return 1 << i;|            ~~^~~~
<source>:12:28:   in 'constexpr' expansion of 'func(-1)'
<source>:6:14: error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]

2.数组越界访问

编译期通过constexpr检测UB写法:

#include <cstdio>constexpr int func(int i)
{int a[32]={};return a[i];
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(32);printf("%d\n",_1);return 0;
}

编译及运行:

<source>: In function 'int main()':
<source>:14:28:   in 'constexpr' expansion of 'func(32)'
<source>:8:15: error: array subscript value '32' is outside the bounds of array 'a' of type 'int [32]'8 |     return a[i];|            ~~~^
<source>:6:9: note: declared here6 |     int a[32]={};|         ^

使用编译期constexpr检测UB:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>constexpr int func(int i)
{int a[32]={};// std::fill(a, a+2,0);//编译期检测:直接访问数组外的数据并不会出错,但是在return中使用就会出错int* b= a+32;*b;return *std::end(a);
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(32);printf("%d\n",_1);return 0;
}

编译以及运行:


<source>: In function 'int main()':
<source>:20:28:   in 'constexpr' expansion of 'func(0)'
<source>:20:30: error: array subscript value '32' is outside the bounds of array type 'int [32]'20 |     constexpr int _1 = func(0);|                              ^
<source>:21:28:   in 'constexpr' expansion of 'func(32)'
<source>:21:31: error: array subscript value '32' is outside the bounds of array type 'int [32]'21 |     constexpr int _2 = func(32);

编译期使用constexpr检测UB行为:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>constexpr int func(int i)
{int a[32]={};return *(char*)a;
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(32);printf("%d\n",_1);return 0;
}

编译及运行:

  • 如果不使用constexpr就无法检测出这个指针强潜在的UB行为
  • C语言可以跨平台,如果Host主机是大端的,而不是小端的,那么强转后的地址一定是数据的低位吗?

<source>: In function 'int main()':
<source>:16:28:   in 'constexpr' expansion of 'func(0)'
<source>:11:13: error: a reinterpret_cast is not a constant expression11 |     return *(char*)a;|             ^~~~~~~~
<source>:17:28:   in 'constexpr' expansion of 'func(32)'
<source>:11:13: error: a reinterpret_cast is not a constant expression

3.有符号数的常量表达式溢出

编译期使用constexpr检测UB行为:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int i)
{ return i + 1;
}int main()
{constexpr int _1 = func(2147483647);constexpr int _2 = func(-2147483648);printf("%d\n",_1);return 0;
}

编译及运行:


<source>: In function 'int main()':
<source>:15:28:   in 'constexpr' expansion of 'func(2147483647)'
<source>:15:39: error: overflow in constant expression [-fpermissive]15 |     constexpr int _1 = func(2147483647);|                                       ^

4.new与delete

使用constexpr检测UB行为:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int n)
{ int *p=new int[n];delete p;return 0;
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译及运行:

<source>: In function 'int main()':
<source>:19:28:   in 'constexpr' expansion of 'func(1)'
<source>:11:12: error: non-array deallocation of object allocated with array allocation11 |     delete p;|            ^
<source>:10:21: note: allocation performed here10 |     int *p=new int[n];|                     ^

使用constexpr检测UB行为:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int n)
{ int *p=new int[n]{};delete[] p;return p[0];
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译及运行:


<source>: In function 'int main()':
<source>:19:28:   in 'constexpr' expansion of 'func(1)'
<source>:19:30: error: use of allocated storage after deallocation in a constant expression19 |     constexpr int _2 = func(1);|                              ^
<source>:10:23: note: allocated here10 |     int *p=new int[n]{};|                       ^

使用constexpr检测UB行为:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int n)
{ int *p=new int[n]{};// delete[] p;return p[0];
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译以及运行:

<source>: In function 'int main()':
<source>:10:23: error: 'func(1)' is not a constant expression because allocated storage has not been deallocated10 |     int *p=new int[n]{};|                       ^

使用智能指针在consexpr中自动析构

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexpr
//return 之后才会调用所有成员的析构函数
constexpr int func(int n)
{ int *p=new int[n]{};struct guard{int* p;constexpr ~guard() noexcept{delete[] p;}}_v(p);return p[0];
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

使用constexpr检测UB:

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int n)
{ int *p=new int[n]{};delete[] p;delete[] p;return p[0];
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译及运行:

<source>: In function 'int main()':
<source>:19:28:   in 'constexpr' expansion of 'func(1)'
<source>:12:14: error: deallocation of already deallocated storage12 |     delete[] p;|        

delete 空指针不会造成UB

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>// #define constexprconstexpr int func(int n)
{ int *p=nullptr;delete p;return 0;
}int main()
{// constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

5.vector

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>// #define constexprconstexpr int func(int n)
{ std::vector<int> v(n);return v[0];
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译及运行:

In file included from /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/vector:66,from <source>:5:
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h: In function 'int main()':
<source>:17:28:   in 'constexpr' expansion of 'func(0)'
<source>:12:15:   in 'constexpr' expansion of 'v.std::vector<int>::operator[](0)'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:1126:41: error: dereferencing a null pointer1126 |         return *(this->_M_impl._M_start + __n);|                 ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~

std::vector v(n);并不将所有成员都初始化为0,vector的resize()方法可以初始化vector内部的成员都初始化为0

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>// #define constexprconstexpr int func(int n)
{ std::vector<int> v(n);v.reserve(2);v.resize(20);return v[0];
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(1);// std::vector<int> v(0);// v.reserve(20);// v.resize(2);// printf("%u\n", v.size());// printf("%u\n", v.capacity());printf("%d\n",_2);return 0;
}

6.空指针解引用

#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>// #define constexprconstexpr int func(int n)
{ int* p=nullptr;return *p;
}int main()
{constexpr int _1 = func(0);constexpr int _2 = func(1);printf("%d\n",_2);return 0;
}

编译及运行:

<source>: In function 'int main()':
<source>:17:28:   in 'constexpr' expansion of 'func(0)'
<source>:17:30: error: dereferencing a null pointer17 |     constexpr int _1 = func(0);|                              ^
<source>:18:28:   in 'constexpr' expansion of 'func(1)'
<source>:18:30: error: dereferencing a null pointer18 |     constexpr int _2 = func(1);|                              ^

参考

  • 【C++20】编译期检测所有未定义行为和内存泄漏,不借助任何外部工具

这篇关于【C++20】编译期检测所有未定义行为undefined behavior和内存泄漏(不借助编译选项以及任何外部工具)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/579722

相关文章

SQLite3命令行工具最佳实践指南

《SQLite3命令行工具最佳实践指南》SQLite3是轻量级嵌入式数据库,无需服务器支持,具备ACID事务与跨平台特性,适用于小型项目和学习,sqlite3.exe作为命令行工具,支持SQL执行、数... 目录1. SQLite3简介和特点2. sqlite3.exe使用概述2.1 sqlite3.exe

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔