小猪猪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、迭代器的更多相关文章

  1. 小猪猪C++笔记基础篇(六)参数传递、函数重载、函数指针、调试帮助

    小猪猪C++笔记基础篇(六) ————参数传递.函数重载.函数指针.调试帮助 关键词:参数传递.函数重载.函数指针.调试帮助 因为一些事情以及自己的懒惰,大概有一个星期没有继续读书了,已经不行了,赶紧 ...

  2. 小猪猪C++笔记基础篇(五)表达式、语句

    小猪猪C++笔记基础篇(五) 关键词:表达式.语句 本章的内容比较简单,基本上没有什么理解上的困难,都是知识上的问题.先开始想要不要写呢,本来是不准备写的,但是既然读了书就要做笔记,还是写一写,毕竟还 ...

  3. C#基础篇四数组

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace P01A ...

  4. java学习笔记(基础篇)—java数组

    一:什么是数组,什么时候使用数组? 数组是用来保存一组数据类型相同的元素的有序集合,数组中的每个数据称为元素.有序集合可以按照顺序或者下标取数组中的元素. 在Java中,数组也是Java对象.数组中的 ...

  5. Python学习笔记——基础篇【第四周】——迭代器&生成器、装饰器、递归、算法、正则表达式

    目录 1.迭代器&生成器 2.装饰器 a.基本装饰器 b.多参数装饰器 3.递归 4.算法基础:二分查找.二维数组转换 5.正则表达式 6.常用模块学习 #作业:计算器开发 a.实现加减成熟及 ...

  6. Python学习笔记基础篇——总览

    Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...

  7. JavaScript笔记基础篇(二)

    基础篇主要是总结一些工作中遇到的技术问题是如何解决的,应为本人属于刚入行阶段技术并非大神如果笔记中有哪些错误,或者自己的一些想法希望大家多多交流互相学习. 1.ToFixed()函数 今天在做Birt ...

  8. java学习笔记-基础篇

    Java基础篇 1—12 常识 13 this关键字 14参数传递 16 继承 17 访问权限 28—31异常 1—12 常识 1.文件夹以列表展示,显示扩展名,在地址栏显示全路径 2.javac编译 ...

  9. Hybrid APP基础篇(四)->JSBridge的原理

    说明 JSBridge实现原理 目录 前言 参考来源 前置技术要求 楔子 原理概述 简介 url scheme介绍 实现流程 实现思路 第一步:设计出一个Native与JS交互的全局桥对象 第二步:J ...

随机推荐

  1. Web学习之自定义标签

    1.编写一个实现Tag接口的Java类(标签处理器类) package me.gacl.web.tag; import java.io.IOException; import javax.servle ...

  2. Maven POM配置释疑

    1.  对于有父子关系的Project, 父POM中依赖使用dependencies 和 dependencyManagement 的区别: dependencies: 即使子项目中不写该依赖项,仍然 ...

  3. 清除缓存、开启IO统计

    SQL性能优化前期准备-清除缓存.开启IO统计 如果需要进行SQl Server下的SQL性能优化,需要准备以下内容: 一.SQL查询分析器设置: 1.开启实际执行计划跟踪. 2.每次执行需优化SQL ...

  4. Eclipse运行慢

    http://blog.csdn.net/chrissata/article/details/7759836 http://blog.csdn.net/heyutao007/article/detai ...

  5. QT实现拖放文件(有例子,并且图文并茂,非常清楚)

    转自:http://my.oschina.net/voler/blog/345722 目录[-] 0. 源代码下载地址 1. 简单文件拖放 2. 复杂文件拖放 3. 通过按钮来完成列表数据的转移 4. ...

  6. 在原有3306端口mysqld服务的情况再搭建第二个3308端口的mysql实例

    1 download the tar.gz [root@472322 tmp]# wget http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6 ...

  7. Linux批量重命名文件

    五种方法实现Linux批量重命名文件 Linux批量重命名文件是指对某些特定的文件统一进行重新命名,以改变原来一批文件的名称,这里介绍五种方法来实现. Linux批量重命名文件会涉及到改变一个字母.改 ...

  8. HDU 3625 Examining the Rooms

    题目大意:有n个房间,n!个钥匙,在房间中,最多可以破k扇门,然后得到其中的钥匙,去开其它的门,但是第一扇门不可以破开,求可以打开所有门的概率. 题解:首先,建立这样的一个模型,题目相当于给出一个图, ...

  9. 第一讲 一个简单的Qt程序分析

    本文概要:通过一个简单的Qt程序来介绍Qt程序编写的基本框架与一些Qt程序中常见的概念 #include <QApplication> #include <QPushButton&g ...

  10. C# Windows Sockets (Winsock) 接口 (转)

    在.Net中,System.Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现.System.Net 命名空间中 ...