编程时踩过的坑(主要是c++)

1.数值溢出

存储数据时要考虑数值溢出的问题,选择合适的类型,特别要注意数值运算时的数值范围是否合适。比方说,如果使用了int类型,尽管数据本身没有超范围,但是在进行乘法计算时却可能会产生数值溢出。

c++类型的数值范围速查表:

TypeTypical Bit WidthTypical Range
char1byte-127 to 127 or 0 to 255
unsigned char1byte0 to 255
signed char1byte-127 to 127
(signed) int4bytes-2147483648 to 2147483647,约10^9,或+/- 2^31
unsigned int4bytes0 to 4294967295,约10^9,或2^32
(signed) short int2bytes-32768 to 32767
unsigned short int2bytes0 to 65,535
(signed) long int4bytes-2,147,483,648 to 2,147,483,647
unsigned long int4bytes0 to 4,294,967,295
(signed) long long8bytes-9223372036854775808 to +9223372036854775807,约10^18,或+/- 2^63
unsigned long long8bytes0 to 1844674407370955161,约10^18,或2^64
float4bytes+/- 3.4e +/- 38 (~7 digits)
double8bytes+/- 1.7e +/- 308 (~15 digits)
long double8bytes+/- 1.7e +/- 308 (~15 digits)
wchar_t2 or 4 bytes1 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;  // 报错

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值