目录结构:

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. 【python】TCP/IP编程

    No1: [TCP] 客户端 import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('www.sina ...

  2. uni-app — 一套前端开发跨平台应用的终极解决方案

    uni-app 是一个使用 Vue.js 开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS.Android.H5.小程序等多个平台. 今天有空就来介绍一下uni-app这个能够跨平台开发, ...

  3. XamarinSQLite教程添加测试数据

    XamarinSQLite教程添加测试数据 此时创建的Students表中是没有任何数据,也就是一个空表.为了方便测试App,开发者需要为表添加一些数据.操作步骤如下. (1)右击创建的Student ...

  4. 前面的内容 也是要去掉白名单 和 8.8.8.8这种非问题IP的 高风险 么? (目前我们没有获取客户的中风险、低风险数据,可以处理掉高风险)

    前面的内容 也是要去掉白名单  和 8.8.8.8这种非问题IP的 高风险 么?   (目前我们没有获取客户的中风险.低风险数据,可以处理掉高风险) == 整体把关.不清楚细节,所以只能从整体决策.做 ...

  5. [python]Flask-migrate简单入门

    Flask-Migrate是用于处理SQLAlchemy 数据库迁移的扩展工具.当Model出现变更的时候,通过migrate去管理数据库变更. Migrate主要有3个动作,init.migrate ...

  6. UOJ.35.[模板]后缀排序(后缀数组 倍增)

    题目链接 论找到一个好的教程的正确性.. 后缀数组 下标从1编号: //299ms 2560kb #include <cstdio> #include <cstring> #i ...

  7. window下安装PIL

    PIL非官方库64 友情连接: https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/ ...

  8. Centos ATI 显卡安装,“LCD 信号超出范围” 解决方法

    centso ATI 显卡驱动安装 centos 版本 32位 6.4 Final ATI 显卡版本:Radeon HD 7400 Series 之前由于很久自己安装了centos显卡,分辨率很低不能 ...

  9. php数组和对象转换函数

    /**  * 数组 转 对象  *  * @param array $arr 数组  * @return object  */ function array_to_object($arr) {     ...

  10. 11-13 js操作css样式

    1.Js操作css样式 Div.style.width=”100px”.在div标签内我们添加了一个style属性,并设定了width值.这种写法会给标签带来大量的style属性,跟实际项目是不符. ...