小猪猪C++笔记基础篇(四)数组、指针、vector、迭代器
小猪猪C++笔记基础篇(四)
关键词:数组,Vector。
一、数组与指针
数组相信大家学过C语言或者其他的语言都不陌生,简单的就是同一个变量类型的一组数据。例如:int a[10],意思就是从a开始有10个连续的int大小的空间。我们还是从初始化说起。
我们以数据类型int为例,当然也可由有很多的数据类型,可以是像int,double这种,也可以是自定义的类,一般的初始化方法有:
int a[10];
int a[10]={0};
int a[3]={0,1,2};
在前面的文章中,有的朋友提到了指针的运算,指针的运算在数组中是非常常见的。我们可以把a[10]看做10个连续的存储空间。那么a其实就是一个int型的指针,存放数组开始的位置,指向a[0]这个元素。那么容易知道a+1就是指向a[1],a+5就是指向a[5]了。于是根据指针的概念我们可以知道:
*a和a[0]是等价的,*(a+5)和a[5]等价的,注意*(a+5)的括号位置,如果你写成*a+5那么结果等价于a[0]+5。写一小段代码测试一下:
int a[6]={1,3,5,7,9,10};
cout<<"a[0]: "<<a[0]<<"*a "<<*a<<endl;
cout<<"a[5]: "<<a[5]<<" *(a+5)"<<*(a+5)<<"*a+5 "<<*a+5<<endl;

结果就是这样的!
那么前面文章中有朋友也提到了,a+1其实加的是步长,并且说a+1等价于a+sizeof(int)那么到底是不是这么回事呢。跟上上面这个初始化过的a,我们再做一个实验:
cout<<sizeof(int)<<endl;
cout<<a<<endl;
cout<<a+1<<endl;
cout<<a+sizeof(int)<<endl;
int b[3]={0};
int c[3]={0};
cout<<b<<" "<<c<<" "<<b-c<<endl;

从结果可以看到,sizeof(int)=4,一个B有8个bit,就是说我们这里的int 是32位。
第二行可以看到a[0]的地址是001FFDF0,a[1]的地址是0001FFDF4,两个之间确实差4个也就是sizeof(int)这么大。
这个比较容易理解,当你把计算机每一位都理解成一个小盒子,一个int要占32个小盒子,那么下一个int的位置就是这么多了。
那么实际上a+1和a+sizeof(int)是不是一样的呢。可以看到a+sizeof(int)=0001FFE00,比a的值大了16,也就是说多了4*sizeof(int)这么大,所以说a+1和a+sizeof(int)是不等价的,其实这也好解释,电脑是这么计算的,sizeof(int)=4,a+4就等于这么多了。
也就是说a的步长确实有sizeof(int)这么大,但是可不能写成a+sizeof(int)。
那我突然就很好奇,两个int *相减会怎么样,我就试了一下。可以发现b放的地址是001FFDDC,c的地址是0001FFDC8。相减的结果是5,也就是5*sizeof(int)。这样就更加好解释为嘛a+1和a+sizeof(int)是不一样的。我们想想啊,(a+1)-a=1,肯定不等于4。
这东西似乎并没有什么卵用,但是明确一下还是好的!
接下来,既然我们知道了数组跟指针是有密切的联系,在实际开发中很多情况我们并知道到底需要用多大的数组,很多情况下我们都喜欢这么干:
Int *a=new int[n];
这实际上跟a[n]并没有什么区别,并且你在调用的时候依然可以用a[1],a[2]这么读取相应地址的元素。我们可以这么说,在数组中间的下标值跟指针后面加个数差不多的意思。所以就不能解释这种变态的问题了p[-2]是个神马东西!
第一次碰到这个问题是在研究生复试的时候一个老师幽幽的问我,p是个数组,那p[-2]是多少?!这不是坑人么,长这么大突然发现数组的下标居然会是负数!!!但是老师果然就是吃的盐比我吃的饭要多,确实是这么回事,我们再写一下段代码来解释:
int a[6]={1,3,5,7,9,10};
int *p=&a[3];
cout<<a[1]<<endl;
cout<<p[-2]<<endl;

P是一个int型指针,位置从a的第四个元素开始,也就是说p[0]=7,p[1]=9,p[2]=10,这样。很容易理解,p=a+3;那么p[-2]=*(p-2)=*(a+3-2)=*(a+1)=a[1]。也就是说p[-2]=*(p-2)。所以如果p-2指向的地址初始化过,那么p[-2]就等于p-2地址指向的值。这种用法是极少的,但是理论就是这样子的,以后万一给我当了老师也可以这么忽悠学生了!
那么指针和数组这个问题我们也讲得差不多了,接下来还有一个很痛苦的问题:什么是指针数组和数组指针。
解决这个问题我们先从多维数组来说起,多维数组就是例如int a[3][4]这样子的东西,实际上我们很少用,因为很容易出错。
int a[3][4]意思就是有个大小为3的数组,每个元素是含有4个元素的数组。我们知道int a[3]里面这个a是一个指针int *,所以int a[3][4]的a可以看成int(*p)[4]这种类型,就是指向有四个元素的指针。那么 int (*p)[4]是指向4个元素的数组,就是我们说的数组指针。P指向一个int *的指针,然后*p指向一个int 型。那么int *p[4]就是一个指针数组,元素放的是指针。实际上这些东西并没有什么用,而且非常容易搞错。还有的人用int **p来表示二维数组,int***p来表示三维数组,虽然是对的,但是看起来怕怕的。
简单的总结一下:
int (*p)[4]是数组指针,指向一个整数数组。调用元素的方法是(*p)[i],值是整数。
int *p[4]是一个指针数组,存放的是指针,调用元素的方法是*p[i],值是指针。那实际上我们是怎么干的呢。两个方法:
第一,做个别名:
typedef int int_arry[4];
int_arry a[3];
第二,算坐标,比如我们申请int a[3][4],实际上可以申请一个int b[3*4]这么大的空间。a[2][3]=b[2*3+3];后面这种方法在图像处理中十分常见。
二、vector和迭代器
按道理来说,这个应该放在后面来讲的。但是它跟数组太像了,我在这里和数组做一个对比。
Vector跟数组很像,我认为它和数组最主要的区别是vector并不需要事先知道要存的这么一串数据的大小,它的空间是动态分配的。我们做一个简单的对比。
int a[6];
vector<int>b;
for(int i=0;i<6;i++)
{
a[i]=i;
b.push_back(i);
}
for(int i=0;i<6;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
for(int i=0;i<6;i++)
{
cout<<b[i]<<" ";
}
cout<<endl;

这里都是把0-5这些数字存进去然后再打印出来。可以看到,vector存的时候是用.push_back(i)来做的,而不是b[i],但是输出的时候跟数组是一样一样的操作。
那么你会问为什么vector不能像数组一样直接调用下标赋值呢。这个很容易解释,因为数组预先开辟了空间,但是vector并没有预先开辟空间。Push_back这个操作其实是先申请一个空间,再把数据放进去。如果我们给没有初始化过的vector元素赋值,会报错,跟数组越界差不多的意思。
但是如果vector的这个原始初始化过了,我们还是可以用b[i]=n,这样的跟数组一样的赋值。
所以调用方法除了初始化部分其他基本一样。当你不确定数组大小的时候可以用vector来解决。当然还有一种替代的方法就是用指针,然后每增加一个元素就动态的申请一空间。具体做法可以参见《数据结构》中的队列或者栈中初始化的操作。
然后我们再说一说迭代器。迭代器有点像指针,或者说指针就是迭代器的一种。当我们调用vec.begin(),vec.end()返回的都是一个迭代器,类似于指针,取值方法也是带星号*vec.begin()这样。基本上指针怎么使,迭代器就怎么使。
最后,再提一提那个负数下标的问题。在vector里面的下标只能值正整数,不能是负数,这是它跟指针的下标的区别。
PS:改了名字,据说写逆袭博士容易遭喷,这其实也是给自己个动力,每天看看书,想想坚持个几年就可以毕业了。不过,还是改名字了。
小猪猪C++笔记基础篇(四)数组、指针、vector、迭代器的更多相关文章
- 小猪猪C++笔记基础篇(六)参数传递、函数重载、函数指针、调试帮助
小猪猪C++笔记基础篇(六) ————参数传递.函数重载.函数指针.调试帮助 关键词:参数传递.函数重载.函数指针.调试帮助 因为一些事情以及自己的懒惰,大概有一个星期没有继续读书了,已经不行了,赶紧 ...
- 小猪猪C++笔记基础篇(五)表达式、语句
小猪猪C++笔记基础篇(五) 关键词:表达式.语句 本章的内容比较简单,基本上没有什么理解上的困难,都是知识上的问题.先开始想要不要写呢,本来是不准备写的,但是既然读了书就要做笔记,还是写一写,毕竟还 ...
- C#基础篇四数组
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace P01A ...
- java学习笔记(基础篇)—java数组
一:什么是数组,什么时候使用数组? 数组是用来保存一组数据类型相同的元素的有序集合,数组中的每个数据称为元素.有序集合可以按照顺序或者下标取数组中的元素. 在Java中,数组也是Java对象.数组中的 ...
- Python学习笔记——基础篇【第四周】——迭代器&生成器、装饰器、递归、算法、正则表达式
目录 1.迭代器&生成器 2.装饰器 a.基本装饰器 b.多参数装饰器 3.递归 4.算法基础:二分查找.二维数组转换 5.正则表达式 6.常用模块学习 #作业:计算器开发 a.实现加减成熟及 ...
- Python学习笔记基础篇——总览
Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...
- JavaScript笔记基础篇(二)
基础篇主要是总结一些工作中遇到的技术问题是如何解决的,应为本人属于刚入行阶段技术并非大神如果笔记中有哪些错误,或者自己的一些想法希望大家多多交流互相学习. 1.ToFixed()函数 今天在做Birt ...
- java学习笔记-基础篇
Java基础篇 1—12 常识 13 this关键字 14参数传递 16 继承 17 访问权限 28—31异常 1—12 常识 1.文件夹以列表展示,显示扩展名,在地址栏显示全路径 2.javac编译 ...
- Hybrid APP基础篇(四)->JSBridge的原理
说明 JSBridge实现原理 目录 前言 参考来源 前置技术要求 楔子 原理概述 简介 url scheme介绍 实现流程 实现思路 第一步:设计出一个Native与JS交互的全局桥对象 第二步:J ...
随机推荐
- Spring--依赖注入
Spring 能有效地组织J2EE应用各层的对象.不管是控 制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.Spring将 ...
- Android 手机上安装并运行 Ubuntu 12.04
ubuntu.sh脚本的原地址变动了,导致下载不了,现在更新了网盘地址.小技巧:遇到一些下载失效的时候可以试一试p2p下载工具(如 easyMule.迅雷等)试一试,说不定有人分享过~* —————— ...
- 记录:sea.js和require.js配置 与 性能对比
最近有点忙,很久无写博客,记录一下之前的配置require.js和sea.js的配置.(有误有望提出 require.js 文件目录 /app(项目使用js) /lib(require.js jq存放 ...
- .net 更改日期格式
示例:更改日期格式 下面的代码示例使用 Regex.Replace 方法将 mm/dd/yy 格式的日期替换为 dd-mm-yy 格式的日期. static string MDYToDMY(strin ...
- 3.PHP 教程_PHP 语法
基础的PHP语法 PHP脚本可以放在文档中的任何位置. PHP脚本以<?php开始,以?>结束: <?php //PHP代码 ?> PHP文件的默认文件扩展名是". ...
- 正则语法笔记-regular expression note
参考文档:python正则表达式 正则表达式定义:正则是一门高度专业编程语言,内嵌在其他语言(python re模块)中使用.正则表达式包含元字符(metacharacter)列表,列表如下: . ^ ...
- KVO 的使用和举例
KVO(key-value Observer),通过命名可以联想到,一个监视着监视着键值配对,让一个对象A来监视另一个对象B中的键值,一旦B中的受监视键所对应的值发生了变化,对象A会进入一个回调函数, ...
- SubLime BracketHighlighter 配置
很多插件在github上都有比较详细的说明 告知安装位置什么的一般来说 插件都是放在Packages目录里面的 从Github上下载 解压得到的文件夹然后放入到Packages中这个目录在哪里呢 ...
- 8,SSO,,eager copy,COW
针对字符串不同的长度,“编译器”选择不同的优化策略:SSO, eager copy,COW,分别针对短字符串,中等长度字符串,长字符串.不过,现在(2016)的大多数编译器(gcc 4.9.1,vs2 ...
- Delphi的MDI编程中遇到的一个奇怪问题(值得研究的一个问题)
近日在用delphi写一个多文档应用程序,除了一个主界面是自动生成的,其他功能页面全部都是通过Application.CreateForm()动态生成的,也就是说在ProjectManager中点击程 ...