《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 安装并启动 ...
随机推荐
- Swift 2.2 最基本的多线程
昨天晚上苹果召开了发布会,第二天除了知道 iPhone SE 和 IOS9.3 之外,你还记住了什么,这一天还是老样子,继续着我们的Swift的基本学习,但出现了许多的警告,进去看看文档宝宝才知道 S ...
- AWS EC2笔记
朋友想搭一个境外网站,找我帮忙,希望服务器.域名都在境外.我没有在境外建站的经历,只能先尝试.于是上网搜索了一下境外服务器,大家比较常用的是Digital Ocean和AWS,我索性打开这两家的官网, ...
- Windows编程坐标系统概念
Windows编程中关于设置映象模式的四个函数 SetWindowOrgSetViewPortOrgSetMapModeSetWindowExtExSetViewPortExtEx 一.Windows ...
- iOS 手机摇一摇功能
调用手机摇一摇功能其实很简单,在你调用的控制器的 viewDidLoad方法里调用 [UIApplication sharedApplication].applicationSupportsShake ...
- 线程协作方法sleep、yield、wait、join
sleep(休眠) 当t线程调用Thread.sleep()时,会让线程t休眠指定时间 Thread.sleep() 和 Thread.currentThread().sleep() 效果 ...
- iOS 快速集成启动页广告
前言 由于项目中要用到启动页广告,所以做了简单的研究,同时借鉴网易新闻和蘑菇街的交互写了一个简单的demo,现在写出来供大家参考,可能由于个人局限会有一些bug和不完善的地方,也希望大家能够友善提醒和 ...
- x86主机搭建家庭智能路由系统 ---- 设计篇
组件简介 Proxmox: 基于Debian的虚拟化系统,支持OpenVZ和KVM. pfSense: 基于FreeBSD的开源路由系统. FreeNAS: 开源NAS系统. shadowsocks: ...
- Java代码审查工具findbugs的使用总结
findbugs简介 Findbugs是一个Java代码静态分析工具,可以用它来检查源代码中可能出现的问题,以期尽可能在项目的初始阶段将代码问题解决. FindBugs检查的是类或者JAR文件即字节代 ...
- ThinkPhp知识大全(非常详细)
php框架 一.真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格 ...
- jQuery入门(一)
相信学js的人多多少少听过JQuery,JQuery对于前端开发人员来说是不可或缺的,他让开发变得更加简单.那到底什么是JQuery呢?用一句话来说,JQuery就是一个javascript的库.所谓 ...