文章目录
- [一、未定义行为Undefined Behavior(UB)](#一、未定义行为Undefined Behavior(UB))
- 参考
一、未定义行为Undefined Behavior(UB)
在C++中,未定义行为(Undefined Behavior)指的是程序的行为没有定义、不可预测或不符合C++标准的情况。当程序中存在未定义行为时,编译器和运行时环境不会对其进行任何保证,可能会导致程序产生意外的结果。
以下是一些常见的导致未定义行为的情况:
-
访问未初始化的变量:如果使用未初始化的变量,其值是不确定的,可能包含任意的垃圾值。
-
数组越界访问:当访问数组时,如果超出了数组的有效索引范围,将导致未定义行为。
-
空指针解引用:当将空指针用作指针解引用,即访问其指向的内存区域时,将导致未定义行为。
-
除以零:在C++中,除以零是一种未定义行为,可能导致程序崩溃或产生无效的结果。
-
使用已释放的内存:如果使用已释放的内存,或者使用指向已释放内存的指针,将导致未定义行为。
-
栈溢出:当递归调用或者使用过多的局部变量导致栈空间耗尽时,将导致未定义行为。
-
多个线程之间的竞争条件:如果多个线程同时访问并修改共享数据而没有适当的同步机制,可能会导致未定义行为。
编译器使用x86_64 gcc13.2
C++版本:-std=c++20
1.返回一个未初始化的局部变量的值
UB写法:
cpp
#include <cstdio>
int func()
{
int i;
return i;
}
int main()
{
int i = func();
printf("%d\n",i);
return 0;
}
编译及运行结果:
bash
Program returned: 0
Program stdout
0
使用编译期constexpr检测UB:
cpp
#include <cstdio>
constexpr int func()
{
int i;
return i;
}
int main()
{
constexpr int i = func();
printf("%d\n",i);
return 0;
}
编译及运行结果:
bash
<source>: In function 'constexpr int func()':
<source>:6:9: error: uninitialized variable 'i' in 'constexpr' function
6 | int i;
| ^
<source>: In function 'int main()':
<source>:12:27: error: 'constexpr int func()' called in a constant expression
12 | constexpr int i = func();
| ~~~~^~
通过constexpr 进行UB检测:
cpp
#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;
}
编译及运行:
bash
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 function
14 | constexpr int _2 = func(-1);
|
编译期使用constexpr 检测UB 写法(关于移位的例子):
- int类型的数据(4Bytes),最多只能移动31bit
cpp
#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;
}
编译及运行:
bash
<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写法:
cpp
#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;
}
编译及运行:
bash
<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 here
6 | int a[32]={};
| ^
使用编译期constexpr检测UB:
cpp
#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;
}
编译以及运行:
bash
<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行为:
cpp
#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主机是大端的,而不是小端的,那么强转后的地址一定是数据的低位吗?
bash
<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 expression
11 | 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行为:
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
<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行为:
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
<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 allocation
11 | delete p;
| ^
<source>:10:21: note: allocation performed here
10 | int *p=new int[n];
| ^
使用constexpr检测UB行为:
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
<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 expression
19 | constexpr int _2 = func(1);
| ^
<source>:10:23: note: allocated here
10 | int *p=new int[n]{};
| ^
使用constexpr检测UB行为:
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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;
}
编译以及运行:
bash
<source>: In function 'int main()':
<source>:10:23: error: 'func(1)' is not a constant expression because allocated storage has not been deallocated
10 | int *p=new int[n]{};
| ^
使用智能指针在consexpr中自动析构
cpp
#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:
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
<source>: In function 'int main()':
<source>:19:28: in 'constexpr' expansion of 'func(1)'
<source>:12:14: error: deallocation of already deallocated storage
12 | delete[] p;
|
delete 空指针不会造成UB
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
// #define constexpr
constexpr 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
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
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 pointer
1126 | return *(this->_M_impl._M_start + __n);
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
std::vector v(n);并不将所有成员都初始化为0,vector的resize()方法可以初始化vector内部的成员都初始化为0
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>
// #define constexpr
constexpr 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.空指针解引用
cpp
#include <cstdio>
#include <cmath>
#include <array>
#include <algorithm>
#include <vector>
// #define constexpr
constexpr 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;
}
编译及运行:
bash
<source>: In function 'int main()':
<source>:17:28: in 'constexpr' expansion of 'func(0)'
<source>:17:30: error: dereferencing a null pointer
17 | constexpr int _1 = func(0);
| ^
<source>:18:28: in 'constexpr' expansion of 'func(1)'
<source>:18:30: error: dereferencing a null pointer
18 | constexpr int _2 = func(1);
| ^