《C++之那些年踩过的坑(三)》
C++之那些年踩过的坑(三)
作者:刘俊延(Alinshans)
本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑。以此作为给自己的警惕。
【版权声明】转载请注明原文来自:http://www.cnblogs.com/GodA/p/6569254.html
前言:
如果你看了我的上一篇博客:《C++之那些年踩过的坑(二)》 我推荐你再看一遍,因为我对内容和排版做了一些修改,尤其是对说的不当的地方进行了修正。
最近挺忙,这篇存了好久,还没发出去,讲多了一下写不完。所以今天就随便聊点简单的东西——unsigned type 。
一、unsigned type 会有什么坑?
你看到这篇的开头,就可能会想,无符号类型能有什么坑呀!那我们就直接了当一些吧!
1、小心可能陷入的死循环
其实单独的 unsigned type 你还是比较容易想明白的,可怕的就是它跟一些其它东西配合(auto),而你忽略了那就是 unsigned type 的时候,例如:
#include <iostream>
#include <cstdlib> int main()
{
char sz[] = "Hello world!";
for (auto i = strlen(sz) - ; i >= ; --i)
{
std::cout << sz[i];
}
}
上述代码逆序输出字符串,有什么问题?一部分人一眼看出了问题,相当一部分人一眼没看出问题,多看两遍终于看出了问题,还有一小部分人到现在还没看出问题。
问题就在于 strlen 返回的类型是 size_t ,它是一个无符号类型,而一个无符号类型永远大于等于 0!是的没错,死循环了。如果你用的是 vs 这种有 intelliSense 的IDE,那还好,鼠标移过去,你能够看到函数的声明。那万一没有这东西呢?又或者,有一些你不认识的函数,那些类型已经被 typedef 到你认不出了,你又如何辨别出来呢?上述的例子非常非常简单,但难免在实际中有可能会出现更复杂的例子,然后你头晕眼花就写出了这样的代码。所以,当你用一个数值作为循环条件,你就要警惕一下,数值表示的范围是什么,循环终止的条件,边界等等。当然,unsigned type 是更容易被忽视的,所以出错的概率更大。
2、小心可能的访问越界
其实上面的例子也是属于访问越界,但C++的数组兼容C,C的数组是不作边界检查的,所以你写出 sz[-1] 这样的代码也不一定会错。所以还是推荐使用C++标准库的容器,对于日常使用,绰绰有余。C++的容器,如果越界了,他就会有提示(Debug模式),比如用 vs 的话,在Debug模式下,越界了他就会有弹出一个对话框显示“xxx subscript out of range”之类的信息。
这个的错法跟上面的其实也差不多,也是写出了类似这样的代码:
std::vector<int> v{ ,,,, };
for (auto i = v.size() - ; i >= ; --i)
{
std::cout << v[i] << " ";
}
之前我写一个 BigInteger 类的时候,也有很多用到这样的倒序输出,一开始我也是这样写,然后一直错。然后我就想肯定是循环下标出了问题的,然后就按 Ctrl+F 查找 for,还好也不是很多,一个一个看,最终才醒悟过来。真是屡踩坑不改~~所以才要记下来让自己印象深刻,下次即使出错也能很快反应过来是错在了什么地方。
二、解决方案
这个 unsigned type 的坑比较简单,主要在 0 这个点容易出错,有这个意识就好了。解决方案(至少)有以下几种:
1、显式指定可以容纳范围的 signed 类型
如果你知道数值的确切范围了,如果可以有一个 signed 类型能容纳它,那么就显式指定出来,比如我知道 vector 最大大小不会超过一百万个,那么我就可以用:
for (int32_t i = v.size() - ; i >= ; --i)
这样来指定类型。
2、在循环内部做检查
如果你不能确定大小,而且不确定 signed type 能否容纳下那个 unsigned type 的全部范围,那就在循环内部做一个检查:
if (v.size() != )
{
for (auto i = v.size() - ; i >= ; --i)
{
std::cout << v[i] << " ";
if (i == )
break;
}
}
3、把边界情况移到循环外部
其实是第2种方法的变形,如果你觉得检查下标浪费了很多性能,那么就可以这样做:
if (v.size() != )
{
for (auto i = v.size() - ; i > ; --i)
{
std::cout << v[i] << " ";
}
std::cout << v[];
}
4、使用迭代器
既然用了C++的容器那么更好的写法当然是这样啦:
for (auto i = v.rbegin(); i != v.rend(); ++i)
{
std::cout << *i << " ";
}
这样就不需要你去考虑下标范围啦,什么检查啦,多方便!如果想写出更泛型,更 C++ Style 的代码,还可以这样:
std::for_each(std::rbegin(v), std::rend(v), [&](int i) {cout << i << " "; });
最终选择哪种就看实际情况和个人爱好咯!
三、其它杂谈
我在网上看到不少关于什么“代码优化技巧”等等文章,即使是最近出的,还是这样写,不知道是不是抄的。比如我看到其中一篇就说到:
有些处理器处理无符号unsigned 整形数的效率远远高于有符号signed整形数
正确与否我先不说了,错别字我也不去说了。留给读者先自己实验一下,我会在下一篇中用一篇来讲这类问题。
四、总结
标题即总结。
《C++之那些年踩过的坑(三)》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- 微信小程序 JS动态修改样式
微信小程序这个坑啊,js动态修改样式,我们并不能用js或者jq 轻轻松松一行代码搞定.或者用removeClass addClass 来修改样式. 以下是一种动态修改样式的方法,原理是绑定数据,然后动 ...
- TFS实现需求工作项自动级联保存
目前在一个大型的金融客户软件研发平台项目实施和支持过程中,客户的质量管理团队基于该平台以及结合其它的平台数据,针对需求管理和业务过程需要拟定了一套完整的需求提出.评审.设计以及实现的流程.基于这套流程 ...
- Android jni 编程3(对基本类型一维整型数组的操作)总结版
主要学习资料:黑马程序员的NDK方法使用(生产类库so) jni编程指南中文版(已上传至博客园) 博主文章(它使用的是VS和eclipse联合开发):http://www.c ...
- SharePoint 2016 配置应用程序商店
最近碰到一个新的需求,就是要给SharePoint配置应用程序商店,挺有意思的,就简单的配置和记录了一下,分享给大家. 其实应用程序商店之前感觉很鸡肋,但是用起来还是不错的.不喜勿喷,呵呵. 首先需要 ...
- SpringMVC:学习笔记(1)——理解MVC及快速入门
SprigMVC-理解MVC及快速入门 说明: 传统MVC-->JSPModel2-->Front Controller + Application Controller + Page C ...
- C++ 头文件系列(fstream)
1. 简介 该头文定义了与文件箱关联的流类的4个模版: basic_filebuf basic_ifstream basic_ofstream basic_fstream 和8个类型: filebuf ...
- js设计模式--单体模式
GOF里的23种设计模式, 也是在软件开发中早就存在并反复使用的模式. 如果程序员没有明确意识到他使用过某些模式, 那么下次他也许会错过更合适的设计 (这段话来自<松本行弘的程序世界>). ...
- BZOJ 3403: [Usaco2009 Open]Cow Line 直线上的牛(模拟)
直接双端队列模拟,完了= = CODE: #include<cstdio>#include<algorithm>#include<iostream>#include ...
- WP8.1开发中ListView控件加载图列表的简单使用(1)
我也是刚接触WP编程没几个月,就是在这段时间一直闲着没事,然后又比较喜欢WP这款系统,就学习了WP这方面的开发言语,自学是很困难的,掌握这方面的资料不多,很初级,就是自己在网上找资料学习过程中,看到别 ...
- 《深入理解Java虚拟机》学习笔记之内存分配
JVM在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域,如下图: 大致可以分为两类:线程私有区域和线程共享区域. 线程私有区域 程序计数器(Program Counter Regi ...