文章目录
局部变量未初始化
编译器规定,编译期是不能触发未定义行为的。加上constexpr
#include <cstdio>
#include <cmath>
#include <array>
//不能在 constexpr 函数中使用非 constexpr 变量
constexpr int func() {
int i;//这里的局部变量未初始化
return i;
}
int main() {
constexpr int i = func(); //constexpr 要求函数在编译期能求值,所以 func() 必须返回一个编译时常量
printf("%d\n", i);
return 0;
}
函数没有返回值
#include <cstdio>
constexpr int func(int i) {
if (i >= 0)
return i;
}
int main() {
constexpr int i = func(-1);
printf("%d\n", i);
return 0;
}
如果在使用函数时,没有使用constexpr,则
#include <cstdio>
constexpr int func(int i) {
if (i >= 0)
return i;
}
int main() {
int i = func(-1);
printf("%d\n", i);
return 0;
}
函数中移位操作检测
#include <cstdio>
constexpr int func(int i) {
return 1<<i;
}
int main() {
constexpr int _1 = func(3); // 2^3 = 8
constexpr int _2 = func(1); // 2^1 = 2
constexpr int _3 = func(-1); // 超出范围,返回 0
constexpr int _4 = func(-3); // 超出范围,返回 0
printf("%d %d %d %d\n", _1, _2, _3, _4);
return 0;
}
左移的量必须在0-31之间(int 只有32bit),否则触发未定义行为
#include <cstdio>
constexpr int func(int i) {
if (i >= 0 && i <= 31)
return 1 << i; // 左移操作,计算 2^i
return 0; // 确保所有路径都有返回值
}
int main() {
constexpr int _1 = func(3); // 2^3 = 8
constexpr int _2 = func(1); // 2^1 = 2
constexpr int _3 = func(-1); // 超出范围,返回 0
constexpr int _4 = func(-3); // 超出范围,返回 0
printf("%d %d %d %d\n", _1, _2, _3, _4);
return 0;
}
检测数组越界
#include <cstdio>
constexpr int func(int i) {
constexpr int a[32] = {
}; // constexpr 变量,保证编译期计算
// if (i < 0 || i >= 32) { // 判断 i 是否在 0~31 之间
// return 0; // 确保所有路径都有返回值
// }
return a[i]; // 返回数组对应元素的值
}
int main() {
constexpr int _1 = func(32); // 超出范围,返回 0
constexpr int _2 = func(31); // 数组索引 31
constexpr int _3 = func(1); // 数组索引 1
constexpr int _4 = func(0); // 数组索引 0
constexpr int _5 = func(-1); // 超出范围,返回 0
constexpr int _6 = func(-31); // 超出范围,返回 0
constexpr int _7 = func(-32); // 超出范围,返回 0
printf("%d\n", _7);
return 0;
}
数组指针加法
数组指针加法,超出了数组的范围也会报错,clang++会直接报错,gcc只在解引用的时候报错
#include <cstdio>
constexpr int func(int i) {
constexpr int a[32] = {
}; // 确保数组在编译期可用
// if (i < 0 || i >= 32) { // 限制 i 在合法范围内
// return 0; // 超出范围返回 0
// }
a+i;
return 0; // 返回数组对应索引的值
}
int main() {
constexpr int _0 = func(33); // 超出范围,返回 0
constexpr int _1 = func(32); // 超出范围,返回 0
constexpr int _2 = func(31); // 访问数组索引 31
constexpr int _3 = func(1); // 访问数组索引 1
constexpr int _4 = func(0); // 访问数组索引 0
constexpr int _5 = func(-1); // 超出范围,返回 0
constexpr int _6 = func(-31); // 超出范围,返回 0
constexpr int _7 = func(-32); // 超出范围,返回 0
constexpr int _8 = func(-33); // 超出范围,返回 0
printf("%d\n", _8);
return 0;
}
#include <cstdio>
constexpr int func(int i) {
constexpr int a[32] = {
}; // 确保数组在编译期可用
// if (i < 0 || i >= 32) { // 限制 i 在合法范围内
// return 0; // 超出范围返回 0
// }
int* p = a+i;
return *p; // 返回数组对应索引的值
}
int main() {
constexpr int _0 = func(33); // 超出范围,返回 0
constexpr int _1 = func(32); // 超出范围,返回 0
constexpr int _2 = func(31); // 访问数组索引 31
constexpr int _3 = func(1); // 访问数组索引 1
constexpr int _4 = func(0); // 访问数组索引 0
constexpr int _5 = func(-1); // 超出范围,返回 0
constexpr int _6 = func(-31); // 超出范围,返回 0
constexpr int _7 = func(-32); // 超出范围,返回 0
constexpr int _8 = func(-33); // 超出范围,返回 0
printf("%d\n", _8);
return 0;
}
std::end()
#include <iostream>
#include <iterator> // std::begin, std::end
int main() {
int a[32] = {
}; // 初始化数组,所有元素默认为 0
std::cout << "数组元素:" << std::endl;
for (int* it = std::begin(a); it != std::end(a); ++it) {
std::cout << *it << " "; // 输出数组的每个元素
}
std::cout << std::endl;
return 0;
}
std::begin(a) 返回数组 a 的首地址(即 &a[0])。
std::end(a) 返回数组 a 尾后位置(即 &a[32],超出数组范围的一个位置)。
it != std::end(a) 避免访问 a[32] 这个非法位置。
使用 std::begin() 和 std::end() 初始化 std::vector
#include <iostream>
#include <vector>
#include <iterator> // std::begin, std::end
int main() {
int a[32] = {
1, 2, 3, 4, 5}; // 只有前 5 个元素初始化,其他默认为 0
std::vector<int> vec(std::begin(a), std::end(a)); // 用数组范围初始化 vector
std::cout << "Vector 中的元素:" << std::endl;
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
std::fill()
#include <iostream>
#include <algorithm> // std::fill
int main() {
int a[32]; // 未初始化,包含垃圾值
std::fill(a, a + 32, 7); // 把数组的所有元素填充为 7
for (int num : a) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
std::fill(a, a + 32, 7); 表示:
- 起始位置:a(即 &a[0])
- 结束位置:a + 32(即 &a[32],超出数组范围的一个位置)
- 填充值:7
该操作将 a[0] 到 a[31] 全部填充为 7。
结合 std::begin() 和 std::end()
#include <iostream>
#include <algorithm> // std::fill
#include <iterator> // std::begin, std::end
int main() {
int a[32];
std::fill(std::begin(a), std::end(a), -1); // 用 -1 填充整个数组
for (int num : a) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
如果想填充部分数组,例如 只填充前 10 个元素:
#include <iostream>
#include <algorithm>
int main() {
int a[32] = {
}; // 默认初始化为 0
std::fill(a, a + 10, 5); // 只填充前 10 个元素
for (int num : a) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
int数溢出与unsigned int数回环,max到min
eg,int数溢出
#include <cstdio>
#include <cmath>
#include <array>
constexpr int add(int i) {
return i + 1;
}
int main() {
constexpr int result = add(2147483647);
printf("%d\n", result); // 输出将是 2147483648(但根据你的系统的 int 大小,可能会发生溢出)
return 0;
}
eg:unsigned int数回环,max到min
#include <cstdio>
#include <cmath>
#include <array>
constexpr unsigned int add(unsigned int i) {
return i + 1; // 完成加法操作
}
int main() {
constexpr unsigned int int_1 = add(0xffffffff); // 0xffffffff 表示最大值
constexpr unsigned int int_2 = add(0xfffffffe); // 0xfffffffe 表示最大值减1
printf("int_1: %u\n", int_1); // 使用 %u 输出无符号整数
printf("int_2: %u\n", int_2); // 输出 int_2 的结果
return 0;
}
死循环
下面的会死循环,int越界是未定义行为
#include <cstdio>
int main() {
for (int t = 1; t != 0; t++) {
// 循环体为空,循环一直执行直到 t 溢出为 0
}
return 0;
}
不会死循环,unsigned int越界,则回到起始位置—多用
#include <cstdio>
int main() {
for (unsigned int t = 1; t != 0; t++) {
// 循环体为空,循环一直执行直到 t 溢出为 0
}
return 0;
}
new和delete不匹配
#include <cstdio>
constexpr int func(int n) {
int* p = new int[n]; // 动态分配内存
// delete[] p; // 正确的删除方式是 delete[],因为我们使用的是 new[]
delete p;
return 0;
}
int main() {
constexpr int result1 = func(1);
int result2 = func(2);
return 0;
}
new一个0大小,但是解引用试图访问
#include <cstdio>
constexpr int func(int n) {
int* p = new int[n]{
};
int ret = *p;
delete[] p;
return ret;
}
int main() {
constexpr int result1 = func(0);
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d %d\n", result1, result2, result3); // 输出结果
return 0;
}
#include <cstdio>
constexpr int func(int n) {
int* p = new int[n]{
};
int ret = p[0];
delete[] p;
return ret;
}
int main() {
constexpr int result1 = func(0);
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d %d\n", result1, result2, result3); // 输出结果
return 0;
}
编译期检测内存没有释放
#include <cstdio>
constexpr int func(int n) {
int* p = new int[n]{
};
return p[0];
}
int main() {
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d\n", result2, result3); // 输出结果
return 0;
}
解决办法
#include <cstdio>
constexpr int func(int n) {
int* p = new int[n]{
};
struct guard{
int* a;
constexpr ~guard() noexcept{
delete [] a;
}
}_{
p};
return p[0];
}
int main() {
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d\n", result2, result3); // 输出结果
return 0;
}
delete nullptr不会出错
#include <cstdio>
constexpr int func(int n) {
int* p =nullptr;
delete p;
return 0;
}
int main() {
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d\n", result2, result3); // 输出结果
return 0;
}
double free
#include <cstdio>
constexpr int func(int n) {
int* p =new int{
};
delete p;
delete p;
return p[0];
}
int main() {
constexpr int result2 = func(1);
constexpr int result3 = func(2);
printf("%d %d\n", result2, result3); // 输出结果
return 0;
}
编译期检测vector
编译期检测vector,长度为0,缺访问其0号元素,越界了
#include <cstdio>
#include <vector>
constexpr int func(int n) {
std::vector<int> a(n);
/*
int*p = nullptr;
p+4;
*/
//报错等价于空指针+0,off by 0可以,off by 1就不信了
return a[0];
//a.front()等价于a[0]
/*
std::vector<int> a;
a.reserve(2);//reserve是未初始化内存
a.reserve(2);//resize用0初始化
a.clear();// clear() 只清空 vector 中的元素,底层内存(capacity)依然保持
*/
}
int main() {
constexpr int result1 = func(0); // 调用 func(0)
constexpr int result2 = func(1); // 调用 func(1)
constexpr int result3 = func(2); // 调用 func(2)
printf("result1: %d, result2: %d, result3: %d\n", result1, result2, result3); // 输出结果
return 0;
}
访问一个已经删除了的指针
#include <cstdio>
#include <vector>
constexpr int func() {
std::vector<int> a{
1,2,3,4};
auto it = a.begin(); // 获取 vector 的第一个元素的迭代器
a.push_back(42); // 向 vector 中添加一个值
return *it; // 返回迭代器指向的元素值
}
int main() {
constexpr int result = func(); // 调用 func() 并获取结果
printf("%d\n", result); // 输出结果
return 0;
}
解决办法:先push
#include <cstdio>
#include <vector>
constexpr int func() {
std::vector<int> a{
1,2,3,4};
a.push_back(42); // 向 vector 中添加一个值
auto it = a.begin(); // 获取 vector 的第一个元素的迭代器
return *it; // 返回迭代器指向的元素值
}
int main() {
constexpr int result = func(); // 调用 func() 并获取结果
printf("%d\n", result); // 输出结果
return 0;
}
解决办法,先reserve预留一些空间
#include <cstdio>
#include <vector>
constexpr int func() {
std::vector<int> a{
1,2,3,4};
a.reserve(5);
auto it = a.begin(); // 获取 vector 的第一个元素的迭代器
a.push_back(42); // 向 vector 中添加一个值
return *it; // 返回迭代器指向的元素值
}
int main() {
constexpr int result = func(); // 调用 func() 并获取结果
printf("%d\n", result); // 输出结果
return 0;
}