《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 安装并启动 ...
随机推荐
- JavaScript嗅探执行神器-sniffer.js,你值得拥有!
一.热身--先看实战代码 a.js 文件 // 定义Wall及内部方法 ;(function(window, FUNC, undefined){ var name = 'wall'; Wall.say ...
- 封装keyframes插件
模仿jquery,使用简单,自动添加浏览器前缀 var keyframes = new SHBKerframes(); keyframes.define([{ name:'myAnimate', 0% ...
- ReentrantLock实现原理
以下是本篇文章的大纲 1 synchronized和lock 1.1 synchronized的局限性 1.2 Lock简介 2 AQS 3 lock()与unlock()实现原理 3.1 基础知识 ...
- 面向UI编程:ui.js 1.1 使用观察者模式完成组件之间数据流转,彻底分离组件之间的耦合,完成组件的高内聚
开头想明确一些概念,因为有些概念不明确会导致很多问题,比如你写这个框架为什么不去解决啥啥啥的问题,哎,心累. 什么是框架? 百度的解释:框架(Framework)是整个或部分系统的可重用设计,表现为一 ...
- 关于AngularJS学习整理---浅谈$scope(作用域) 新手必备!
作为初次接触 AngularJS的新手,想要深层理解里面的内容短时间还是不可能的,所以标题写了浅谈字样,以下内容是参考各位大神以及相关书籍整理加个人理解,出现错误的地方请大家指正. $scope(作用 ...
- JUnit与JMock学习
JUnit与JMock学习 测试驱动编程和持续集成部署应该说是现在软件开发者的必备武器,不过跟其他很多好东西一样,在我们公司的推广总要慢上一拍,毕竟老板看的是你能够把功能实现好让客户满意,所以能不折腾 ...
- esri-leaflet入门教程(4)-加载各类图层
esri-leaflet入门教程(4)-加载各类图层 by 李远祥 在leaflet中图层一般分为底图(Basemap)和叠加图层(Overlay).前面章节已经介绍过底图其实也是实现了TileLay ...
- CREELINKS平台_处理器CeAd资源使用说明(CeAd的配置与使用)
0x00 CREELINKS平台简介 CREELINKS(创e联)是由大信科技有限公司研发,集合软硬件.操作系统.数据云储存.开发工具于一体,用于物联网产品的设计.研发与生产的平台. 平 ...
- 关于Console控制台输出的玩法
你在浏览网页的时候,是否注意过这些网页的控制台输出了什么? Console这种东西,其实一般只有前端工作者才会注意到.console在我们实际开发中可是个宝贝,他是各种error和warning的展示 ...
- EF CodeFirst下数据库更新
用EF Code first模式来开发系统,可使用Migrations命令来让数据库自动更新 1.在VS->工具->库程序包管理器->程序包管理控制台 中执行 Enable-Migr ...