本文主要是介绍【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和内存泄漏(不借助编译选项以及任何外部工具)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!