1.数值溢出
存储数据时要考虑数值溢出的问题,选择合适的类型,特别要注意数值运算时的数值范围是否合适。比方说,如果使用了int类型,尽管数据本身没有超范围,但是在进行乘法计算时却可能会产生数值溢出。
c++类型的数值范围速查表:
Type | Typical Bit Width | Typical Range |
---|---|---|
char | 1byte | -127 to 127 or 0 to 255 |
unsigned char | 1byte | 0 to 255 |
signed char | 1byte | -127 to 127 |
(signed) int | 4bytes | -2147483648 to 2147483647,约10^9,或+/- 2^31 |
unsigned int | 4bytes | 0 to 4294967295,约10^9,或2^32 |
(signed) short int | 2bytes | -32768 to 32767 |
unsigned short int | 2bytes | 0 to 65,535 |
(signed) long int | 4bytes | -2,147,483,648 to 2,147,483,647 |
unsigned long int | 4bytes | 0 to 4,294,967,295 |
(signed) long long | 8bytes | -9223372036854775808 to +9223372036854775807,约10^18,或+/- 2^63 |
unsigned long long | 8bytes | 0 to 1844674407370955161,约10^18,或2^64 |
float | 4bytes | +/- 3.4e +/- 38 (~7 digits) |
double | 8bytes | +/- 1.7e +/- 308 (~15 digits) |
long double | 8bytes | +/- 1.7e +/- 308 (~15 digits) |
wchar_t | 2 or 4 bytes | 1 wide character |
2.memset初始化
在做一些编程题时,有时会定义一个全局的大数组并重复使用,这时要记得在每个case开始时用memset函数初始化一下。否则会产生很隐蔽的bug。
3.函数栈溢出
我们知道函数的局部变量是存储在栈空间里的,而栈空间一般只有4Mb或者8Mb(跟系统有关)。所以当函数局部变量定义得过大(例如一个大数组),就很容易导致栈溢出错误。除此之外,函数的递归深度过大也会导致栈溢出。
这里复习一下,一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack):又编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。
2、堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。
3、全局区(static):也叫静态数据内存空间,存储全局变量和静态变量,全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量放一块区域,没有初始化的在相邻的另一块区域,程序结束后由系统释放。
4、文字常量区:常量字符串就是放在这里,程序结束后由系统释放。
5、程序代码区:存放函数体的二进制代码。
4.make_pair出错
string str = "asd";
int pos = 1;
make_pair<string, int>(str, pos);
上面的代码会报错:no matching function for call to 'make_pair(std::string&, size_t&)',这是因为make_pair在c++11前后修改了声明:
// 定义于头文件 <utility>
// C++11前
template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );
// C++11起,C++14前
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
// C++14起
template< class T1, class T2 >
constexpr std::pair<V1,V2> make_pair( T1&& t, T2&& u );
对于该错误,有两种修改方式:
1、make_pair(str, pos)
2、make_pair<string, int>(string(str), int(pos))
5. STL unordered_map添加元素
unordered_map有两种添加元素的方法,分别为下标操作或成员函数insert,区别如下。
下标操作:如果key存在,则更新value;如果key不存在,就创建key-value对。实例:map[key] = value.
insert:如果key存在,则什么也不做;如果key不存在,就创建key-value对。实例: map.insert((key, value)).
注意insert有类型为pair的返回值,pair.first是一个迭代器,如果元素插入成功则返回的迭代器指向关键字位置。pair.second是一个bool类型变量,如果待插入的key不存在,即元素插入成功,则为true;否则为false.
6. size_type类型
这一类型通常在调用STL容器或者std::string的size()函数时返回,属于无符号的整型,具体的最大范围由机器环境决定。
一个常见的错误是:
// int count;
// vector<int> vec;
while (vec.size() - 1 < count){
// TODO
}
如果vec.size()等于0,那么vec.size() - 1就会出现数值溢出的情况,并且不会报错。
7. STL multiset / multimap 删除元素
STL中 multiset / multimap 允许保留重复的元素,但是在使用成员函数erase删除元素时需要注意:
如果输入的是键值,则删除所有等于该键值的元素;如果输入迭代器指针,则删除指针所对应的单个元素。
PS. erase函数不能输入反向迭代器指针。
multiset<int> ms;
ms.insert(1); ms.insert(1); ms.insert(1);
ms.insert(2); ms.insert(3);
ms.erase(ms.begin()); // 删除单个元素
ms.erase(1); // 删除所有值等于1的元素
ms.erase(ms.rbegin()); // 报错,不能用reverse_iterator
ms.erase(ms.rend()); // 报错,不能用reverse_iterator
7. STL set / multiset / map /multimap 细节
这四个数据结构都是采用RB-Tree实现。
比较操作的重载
不能直接用bool函数,可以写成结构体。
struct cmp{
bool operator () (const int& x, const int& y)const {
return x < y;
}
};
multiset<int, cmp> ms; // 注意这里的cmp不能直接传bool函数
迭代器
迭代器可以用自增、自减操作,不能用加减操作。
multiset<int> ms;
auto it = ms.end();
it--; // 成功
it = it - 1; // 报错