在C++中,通过在变量声明和变量名字后跟一对方括号就可以声明一个数组,但需要同时在方括号中给定数组所包含的元素个数。这里给出了一个一维数组的定义,其中包含了10个int型元素值:
int fibonacci[10];
可以采用fibonacci[0],fibonacci[1],...,fibonacci[9]的形式对这些元素进行访问。通常情况下,我们希望以定义的方式来初始化数组:
int fibonacci[10] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
在这种情况下,可以忽略数组的大小,因为编译器能从数组的初始状态推算出元素的个数:
int fibonacci[] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
静态初始化也可用于复杂数据结构类型,比如Point2D:
Point2D triangle[] = {Point2D (0.0, 0.0), Point2D(1.0,0.0), Point2D(0.5,0.866)};
如果我们无意在后续程序中修改数组,则可以让它成为常量数组:
const int fibonacci[] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
要找出一个数组中包含的元素个数,可以像下面这样使用sizeof()运算符:
int n = sizeof(fibonacci) / sizeof(fibonacci[0]);
sizeof ()运算符以字节为单位返回它的参数的大小值。一个数组中元素的个数就是它的字节数除以其中一个元素的字节数。因为这一方法非常麻烦,所以,一种更为常用的方法就是先定义一个常量,然后用这个常量来定义数组:
enum {NFibonacci = 10};
const int fibonacci[NFibonacci ] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
这里的本意是想把该常量声明成const int型变量。但遗憾的是,一些编译器已经可以用const变量作为数组大小的指示标记。本附录的后面会解释enum关键字。
数组的遍历通常是使用整数来完成的。例如:
const int *ptr = &fibonacci[0];
while( ptr != &fibonacci[10])
{
std::cout<<*ptr<<std::endl;
++ptr;
}
我们用第一个元素的地址来初始化这个指针,并且一直循环到“最后一个元素之后”的元素(即“第11个”元素,fibonacci[10])。在每一次遍历中,“++”运算符就会把指针向后移动一次而指到下一个元素处。
不使用&fibonacci[0],应当也可以完成对fibonacci的写操作。这是因为单独使用一个数组的名字会自动转换为指向该数组中的第一个元素的指针。与此相似,可以使用fibonacci +10 代替 &fibonacci[10]。这种方式也能够很好地工作:我们可以使用*ptr或者ptr[0]获得当前元素的内容,并且可以通过*(ptr+1)或者ptr[1]访问下一个元素。这一原理有时称为“指针和数组的等价性”。
为了避免出现无故的低效率,C++不允许我们给函数传递数组的值。相反,它们必须以地址的形式传递。例如:
#include <iosrtream>
void printIntegerTable(const int *table, int size)
{
for(int i = 0;; i<size; ++i)
{
std::cout<<table[i]<<std::endl;
}
}
int main()
{
const int fibonacci[10] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
printIntegerTable(fibonacci, 10);
return 0;
}
具有讽刺意味的是,尽管C++没有给我们任何选择的余地,而不管我们是以地址的方式还是以值的方式来传递一个数组,但它还是在用于声明参数类型的语法中给了我们一些自由。使用的不是const int *table,我们也本可以写作 const int table[] 的方式来声明一个指针pointer 到 constant int 的参数。与之相似的是,用于main()中的arg参数则可以声明为char *argv[] 或者char **argv。
要把一个数组复制到另一个数组,一种方法是在这个数组中进行循环:
const int fibonacci[NFibonacci] = {0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 ,34};
int temp[NFibonacci];
for(int i = 0; i<NFibonacci;i++)
{
temp[i] = fibonacci[i];
}
对于像int这样的基本数据类型,也可以使用std::memcpy(),它可以复制一块内存中的数据。例如:
std::memcpy(temp, fibonacci, sizeof(fibonacci));
当声明一个C++数组的时候,数组的大小必须是一个常数值。如果希望创建一个可变大小的数组,可以有多种方法。
- 动态分配该数组;
int *fibonacci = new int[n]
这个new[]运算符会在内存的连续位置分配一定数量的元素,并且可以返回一个指向第一个元素的指针。由“指针和数组的等价性”原理可知,可以通过该指针访问元素fibonacci[0], fibonacci[1], ... , fibonacci[n-1]。当使用完数组后,应当使用运算符delete[ ] 释放它所占用掉的内存空间:
delete[ ] fibonacci;
- 使用标准的std::vector<T类>
#include <vector>
std::vector<int> fibonacci(n);
使用[ ]运算符可以访问各个元素,就像访问普通C++数组一样。利用std::vector<T>(这里的T是存储在向量中的元素类型值),可以在任何时候使用resize()改变这个数组的大小,并且可以使用赋值运算符复制它。在类的名字中包含尖括号的类称为模板类。
- 使用Qt的QVector<T>
#include <QVector>
QVector<int> fibonacci(n);
QVector<T>的API与std::vector<T>的API类似,但使用Qt的foreach关键字,它还可以支持遍历操作,并且可以使用隐式数据共享(“写时复制”)作为内存和速度的优化技术。第11章给出了Qt的容器类并且讲述了它们与标准C++容器类的联系。
无论何时,或许都应当力求避免使用内置数组而应当尽量用std::vector<T>或者QVector<T>来替代它们。尽管如此,还是很有必要理解内置数组究竟是如何工作的,因为迟早或许还是希望能够把它们放进高度优化的代码中,或者还是需要利用它们与现有的C函数库进行交流。
网友评论