【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++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python脚本轻松实现检测麦克风功能

《Python脚本轻松实现检测麦克风功能》在进行音频处理或开发需要使用麦克风的应用程序时,确保麦克风功能正常是非常重要的,本文将介绍一个简单的Python脚本,能够帮助我们检测本地麦克风的功能,需要的... 目录轻松检测麦克风功能脚本介绍一、python环境准备二、代码解析三、使用方法四、知识扩展轻松检测麦