目录结构:

contents structure [-]

数组是存放相同类型的容器,数组的大小确定不变,不能随意向数组中添加元素。因为数组的大小固定,因此对某些特殊应用来说程序的运行时性能较好,但相应的损失了一些灵活性。

1. 一维数组

1.1 数组的定义和初始化

一维数组的声明形如:a[b],其中a是数组的名字,b是数组的维度(数组中元素的个数)。其实这里说成数组的定义更加准确,因为a[b];是会被分配内存的。
比如:

int arr[]; //arr是含有10个整型的数组
int arr[]={}; //arr是含有10个整型的数组,进行了初始化,每个元素的值都是10
int *arr[]; //arr是含有10个整型指针的数组
int arr[] = {,,}; //arr是含有3个整型的数组
int arr[] = {,,}; //等价于int arr[5] = {1,2,3,0,0}
string arr[] = {"hello","world"}; //等价于 string arr[3] = {"hello","world",""};
char arr[] = "hello"; //这里不能声明维度为5,因为字符串末尾还有一个空字符('\0'),所以应该是"hello\0"
char arr[] = {'h','e','l','l','o','\0'}; //等价于 char arr[] = "hello";
char arr[] = {'h','e','l','l','o'}; //这条语句和上面一条语句是一样的。默认字符初始化值是'\0'

如果在定义了整型类型的数组后,并没有对内存进行初始化的话,那么里面会存放以前的垃圾值。用字符串字面值初始化字符数组时,一定要注意字符串字面值的末尾还有一个空字符。

上面介绍一些简单的一维数组的定义,下面结合引用和指针来介绍一些复杂的数组声明,理解数组声明格式的步骤:默认顺序从右到左,如果有括号,要先看括号里的。

int *arr1[];//
int (*arr2)[];//

第1条语句和第2条语句的含义完全不同,第1条语句表示:“arr1是含有10个整型指针的数组”;第2条语句的含义是:“arr2是指向一个10个整型大小的指针”。也就是说,arr1是一个数组,数组的大小是10,数组中每个元素类型是int类型的指针;arr2首先是一个指针,指向了大小为10的数组,数组中每个元素是int类型。

int &arr3[];//3,错误
int (&arr4)[];//4,错误
int a[] = {};
int (&arr5)[] = a;//

arr3的本意是想声明了一个数组,数组的大小是10,数组中的每个元素的类型是一个int类型的引用,但是c++并不存在这种数组的声明格式,语法错误。
arr4的本意是想声明一个引用,引用的是一个数组,数组的大小是10,数组中每个元素的类型是int类型,但arr4既然是一个引用,所以必需要初始化,所以arr4错误,arr5正确。

还可以结合指针和引用来共同声明更复杂的数组,理解的方式和上面都是一样的(从右到左,有括号先看括号),例如:

int *(&arr)[] = ...;//必须要进行正确的初始化

上面一条语句的含义:arr首先是一个引用,引用的类型是一个大小为10的数组,数组的每个元素是int*(整型指针)类型。因为arr是一个引用,所以这条语句必须要初始化,否则会报错。

1.2 数组元素的访问

数组的下标是从0开始的,在使用数组下标的时候,通常将其定义为size_t类型(一般不要使用int类型),size_t位于cstddef头文件(该文件是C标准库stddef.h头文件的C++版本)中,是无符号整型的别名,可以代表任意字节对象的大小。
例如:

#include <cstddef>
#include <iostream>
using namespace std; int main(){
size_t size = ;
int arr[size] = {};
for(size_t index=; index < size; index++){
cout << arr[index] << " ";
}
cout << endl;
return ;
}

C++11标准提供了auto关键字:

for(auto a : arr)
cout << a << " ";

1.3 数组和指针

在c/c++语言中,数组和指针有着非常紧密的联系,在使用数组编译的时候通常会把它转化为它的第一个元素的指针。

例如:

string nums[] = {"one","two","three"};
string *p = &nums[];
string *p2 = nums;

p和p2其实等价的,都是表示指向nums首元素的指针。

int ia[] = {,,,,,};
auto ia2(ia);//ia2是一个整型指针,指向ia的第一个元素

ia是含有6个整数的数组,但当使用ia作为ia2的初始值时,编译器实际的编译类型是:

auto ia2(&ia[]);

可以看出,ia2是一个指针,指向了ia数组的第一个元素。

使用auto会发生上述的转化,但当使用decltype关键字时上述转化不会发生

decltype(ia) ia3 = {,,,,,};//ia3是一个含有6个整数的数组

其中ia是一个含有6个整数的数组int[6]类型,decltype(ia)得到的类型也是int[6],所以ia3的类型也是int[6]。

上面提到了如何获取数组第一个元素的指针,接下来讨论一下如何获取数组尾元素下一元素的指针

string *p = &nums[];//nums的大小为3,p指向数组尾元素的下一个元素。

对于获取首元素和尾元素下一元素的指针,c++11提供了两个新方法,begin()和end()方法

#include <iostream>
using namespace std;
int main(){
int ia[] = {,,,,,,,,};
int *p1 = begin(ia);//获取首元素的指针
int *p2 = end(ia);//获取尾元素下一元素的指针
for(; p1 != p2 ; p1++)
cout << *p1 << " ";
cout << endl;
}

指向数组元素的两个指针可以进行解运算、比较、递增、递减、指针相减等操作。指针的比较就是比较地址的大小。递增/递减就是内存地址往后/往前移动1,除此之外,还可以在指针上加上/减去某整数n,表示往后/往前移动n位的新指针。指针相减就是内存地址相减,差值表示间隔的元素个数。

两个指针相减是一种ptrdiff_t的标准库类型,和size_t一样,ptrdiff_t也是定义在cstddef头文件中的。因为差值可能为负,所以ptrdiff_t是一种有符号整型。

auto n = end(ia) - begin(ia);//等价于:ptrdiff_t n = end(ia) - begin(ia);

例如:

#include <iostream>
#include <cstddef>
using namespace std;
int main(){
int ia[] = {,,,,,,,,};
auto p1 = begin(ia);//首元素的指针
auto p2 = end(ia);//尾元素下一元素的指针
while(p1 < p2){
*p1 = (*p1) * ;
p1++;
}
ptrdiff_t length = end(ia) - begin(ia);//得到ia数组的长度,ptrdiff_t是指针相减的类型(一种有符号整数的别名)
for(size_t index=; index<length; index++)
cout << ia[index] << " ";
cout << "\n";
int *p3 = &ia[];//得到第5个元素
cout << "第7个元素 : " << *(p3 + ) << "\n";
cout << "第3个元素 : " << *(p3 - ) << endl;
return ;
}

结果:

第7个元素 :
第3个元素 :

当一个指针指向一个数组后,使用指针可以直接通过下标访问数组中的值:

int a[]={,,,,,};
int *p=a;//指向a的首元素 for(int index=; index<; index++)
  cout << p[index] << " ";//等同于 *(p+index) cout << endl;

注意:p[index]是未移动指针p的位置的,指针p始终都未改变(p一直都指向数组a的首元素)。

C++数组的初始化长度不能是变量, 如果需要变量长度来初始化数组,那么这时候可以new一个数组,然后用指针指向数组来解决这个问题:

    std::string s = "Hello World!";
char *cstr = new char[s.size() + ];//不能写成char cstr[s.size()+1]
#注意:在不使用cstr时,一定要手动delete cstr.

2. 多维数组

严格的说,在C++语言中是没有多维数组的,通常所说的多维数组其实就是数组的数组。

当一个数组中的元素依然是一个数组时,通常使用两个维度来定义它。

int arr1[m][n];//二维数组,大小为m的数组,每个数组的元素都是大小为n的数组。
int arr2[m][n][q];//三维数组,大小为m的数组,每个数组的元素都是大小为n的数组,然后大小为n的数组中的每个元素又是大小为q的数组。

对二维数组来说,通常把第一维度称为行,把第二维度称为列,因此上面的arr1是一个含有m行n列的二维数组。

int ia1[][]={
{,,,},
{,,,},
{,,,},
}
int ia2[][]={,,,,,,,,,,,};
//显式初始化每行的首元素
int ia3[][]={{},{},{}};
//显式初始化第一行元素
int ia4[][]={,,,};

上面的ia1和ia2是等价的,ia1分配了12个整型内存空间,由于这些空间都是连续的(因为数组就是一块连续的内存空间),所以换成ia2的声明方式完全一样。

可以使用下标运算符来访问多维数组的元素,此时数组的每个维度对应一个下标运算符。

// 用arr的首元素为ia的最后一行的最后一列元素赋值
ia[][] = arr[][][];//ia是一个3行4列的数组,arr是一个三维数组
int (&row)[] = ia[];//把ia的第二行绑定到引用a上

例如:

#include <iostream>
using namespace std;
int main(){
int ia[][] = {{,,,},{,,,},{,,,}};
   //使用下标运算符
for(size_t row=; row < ; row++)
for(size_t col=; col<; col++)
cout << ia[row][col]<< " ";
cout << "\n";    //使用指针
for(int (*row)[] = ia; row != ia + ; row++)
for(int *col = *row; col != *row + ; col++)
cout << *col << " ";
cout << "\n";    //使用指针
for(int *val : ia)
cout << *val << " ";
cout << "\n";    //使用引用
for(auto &row : ia)
for(auto &col : row)
cout << col << " ";
cout << endl;
return ;
}

注意,上面的auto循环不能写成

for(auto row : ia){
for(auto col : row){//错误,因为row是int*类型
}
}

因为数组名在使用的时候,默认是会被编译成指向首元素的指针。
换句话说,循环语句for(auto row : ia);其中的 row 是int *类型,对于for(auto col : row)的话,显然错误。使用auto &row 让编译器知道自己要使用的是引用,编译器就不会再把它转化为指针。

对于获取首元素和尾元素下一元素的指针,可以通过标准库中的begin()和end()方法。

【C++】C++中的数组的更多相关文章

  1. 前端开发:Javascript中的数组,常用方法解析

    前端开发:Javascript中的数组,常用方法解析 前言 Array是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array ...

  2. JavaScript jQuery 中定义数组与操作及jquery数组操作

    首先给大家介绍javascript jquery中定义数组与操作的相关知识,具体内容如下所示: 1.认识数组 数组就是某类数据的集合,数据类型可以是整型.字符串.甚至是对象Javascript不支持多 ...

  3. java 在循环中删除数组元素

    在写代码中经常会遇到需要在数组循环中删除数组元素的情况,但删除会导致数组长度变化. package com.fortunedr.thirdReport; import java.util.ArrayL ...

  4. Objective-C中把数组中字典中的数据转换成URL

    可能上面的标题有些拗口,学过PHP的小伙伴们都知道,PHP中的数组的下标是允许我们自定义的,PHP中的数组确切的说就是键值对.而在OC我们要用字典(Dictionary)来存储,当然了Java用的是M ...

  5. GCC 中零长数组与变长数组

    前两天看程序,发现在某个函数中有下面这段程序: int n; //define a variable n int array[n]; //define an array with length n 在 ...

  6. C++中的数组

    数组名作为参数时,传递的是数组的首地址, 主调函数中实参数组元素个数不应该少于形参数组的元素个数 把数组名作为参数时,一般不指定数组第一维的大小 即使指定,编译时也会被忽略的.

  7. javascript中关于数组的一些鄙视题

    一.判断一个数组中是否有相同的元素 /* * 判断数组中是否有相同的元素的代码 */ // 方案一 function isRepeat1(arrs) { if(arrs.length > 0) ...

  8. Oc中的数组

    ========================== 数组 ========================== 一.认识数组 oc中可以把NSObject对象的子类放到数组这个集合中,但是int.f ...

  9. Javascript中判断数组的正确姿势

    在 Javascript 中,如何判断一个变量是否是数组? 最好的方式是用 ES5 提供的 Array.isArray() 方法(毕竟原生的才是最屌的): var a = [0, 1, 2]; con ...

  10. C#中的数组,多维数组和交错数组

    想研究一些面向对象的东西,也许是代码写得还不够多.感觉还不好,看那些教程,不是嫌太水就是太难看不懂.心情很是落寞 不过再怎样也要坚持每天发一篇博客. 这篇来说一下C#中的数组,多维数组,交错数组的一些 ...

随机推荐

  1. MLR:利用多元线性回归法,从大量数据中提取五个因变量来预测一个自变量—Jason niu

    from numpy import genfromtxt from sklearn import linear_model datapath=r"Delivery_Dummy.csv&quo ...

  2. Ultra-QuickSort POJ - 2299 (逆序对)

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a seque ...

  3. HDU 3594 Cactus (强连通+仙人掌图)

    <题目链接> <转载于 >>> > 题目大意: 给你一个图,让你判断他是不是仙人掌图. 仙人掌图的条件是: 1.是强连通图. 2.每条边在仙人掌图中只属于一个 ...

  4. vsftpd中配置文件详解

    在vsftp服务器中,配置文件/etc/vsftpd/vsftpd.conf文件是配置的核心内容,其具体的配置信息详细情况如下: 1.默认配置: 1>允许匿名用户和本地用户登陆. anonymo ...

  5. SpringBoot使用LomBok

    Lombok是什么?它是一个能帮我们消除那些必须要写但是重复的代码,比如setter,getter,构造函数之类的方法. 首先先简单说一下idea安装lombok,有2种方法: 1.直接从http:/ ...

  6. Adams/Car与Simulink联合仿真方法

    必须是Assembly装配体才行,并支持仿真设置.这里使用MDI_Demo_Vehicle模型,输出前缀为test1,输出选择files_only.然后OK输出. 生成的文件如下: 在Plant Ex ...

  7. Android异步消息传递机制源码分析

    1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...

  8. Shell脚本笔记(四)条件判断

    条件判断 一.条件测试与比较 一)条件测试基础语法 下列测试语法中如果测试的表达式成立,条件测试就会结束,并返回0. 1.test条件测试 语法:test  <测试表达式> 2.中括号条件 ...

  9. 潭州课堂25班:Ph201805201 django框架 第十课 GET,POST 请求 文件上传,HttpResponse,cookie (课堂笔记)

    在项目中新建个APP, 在主目录中的配置文件中进行 APP 注册 在主目录中的 urls 文件中进行路径分配 新建 urrls 文件 进行分路由配置 创建模板文件 html 文件 写个 form 表单 ...

  10. AC自动机相关Fail树和Trie图相关基础知识

    装载自55242字符串AC自动机专栏 fail树 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账- 有了这个东西, ...