资源访问校验
当外部数据参与数组索引、内存申请大小、内存复制长度、指针偏移等操作,必须对数据大小进行严格校验,否则可能发生严重问题。
see also 202306272237整数运算
在传递数组时,同时传递数组长度
数组在函数传参时会退化为指针,导致数组长度信息丢失,容易造成数组索引合法性检查错误,引发越界读写问题。
void test()
{
char array[10];
init_array(array);
}
void init_array(char arr[])
{
// 【错误】这里计算的是 char* 指针的大小
for (size_t i = 0; i < sizeof(arr); ++i) ...
}
建议:
- 如果函数只接受固定长度的数组作为参数,可以定义参数类型为数组引用或std::array
- 如果函数要兼容C接口,那么应该把长度作为另外一个参数也传递进去
void init_array(char arr[], size_t len); // ok
void init_arr(char arr[]) // error, arr deduced to char*
{
cout << "nake arr" << sizeof(arr) << endl;
}
void init_arr_ref(char (&arr_ref)[10]) // ok, pass by ref
{
cout << "arr ref" << sizeof(arr_ref) << endl;
}
template<typename T, size_t N>
void init_arr_temp(T (&arr_temp)[N]) // ok, template instantiate to init_arr_temp<char, 10UL>(char (&arr_temp)[10])
{
cout << "arr temp" << sizeof(arr_temp) << endl;
}
RAII管理资源的声明周期
资源的获取和释放是成对操作(例如new/delete,fopen/fclose,lock/unlock等)恰好能对应c++语言对称的构造函数和析构函数。利用c++对象的生命周期来管理资源的声明周期,是一种常见的策略。
不要访问超出生命周期的对象
禁止将局部变量的地址返回到其作用域以外,
string_view get_data()
{
const char s[] = "hello world";
return s; // 【错误】,会间接将s的地址返回到其作用域外
}
当lambda会逃逸处函数外面时,禁止按引用捕获局部变量,
void foo()
{
int local_var = 0;
...
// 【错误】,lambda不确定何时、在何地(线程)执行,而local_val的生命周期在此处即将结束
thread_pool.QueueWork([&] { process(local_val); });
}