【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

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

基于Python打造一个全能文本处理工具

《基于Python打造一个全能文本处理工具》:本文主要介绍一个基于Python+Tkinter开发的全功能本地化文本处理工具,它不仅具备基础的格式转换功能,更集成了中文特色处理等实用功能,有需要的... 目录1. 概述:当文本处理遇上python图形界面2. 功能全景图:六大核心模块解析3.运行效果4. 相

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in