1. 定义数组程序员
数组是由类型名,标识符和维数组成的复合数据类型。其中,类型名能够是内置类型或类类型,除引用以外,数组元素的类型还能够是任意的复合类型。数组
数组的维数必须用大于等于1的常量表达式定义。在定义数组时,可为其元素提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表:安全
const unsigned array_size = 3; int ia[array_size] = {0, 1, 2};
若是没有显式提供元素初值,则数组元素会像普通变量同样初始化。iphone
对于数组元素的显式初始化应当注意两点:函数
第一,显式初始化的数组不需指定数组维数值,例如ui
int ia[] = {2, 22, 222};
第二,若是指定维数大于列出的元素初值数,对剩余元素来讲,内置类型初始化为零,类类型调用默认构造函数初始化。例如spa
double coords[3] = {}; // all elements default initialized to 0 int nums[5] = {0, 1, 2}; // nums[3] and nums[4] default initialized to 0 // lables[1] and labels[2] default initialized to empty string string labels[3] = {"Hello"};
总结来看,内置类型的数组,在定义后要进行初始化,对于显式初始化方式,一旦指定了其维数可确保全部元素都会被初始化,即便初始化列表中指定的元素初值数不足。而对于类类型的数组来讲,在定义时就会调用默认构造函数初始化,若是显式初始化,则调用相应构造函数进行初始化。例以下面本身写的一个简单类数组:指针
#include using namespace std; class Apple { public: Apple(int startVer):m_version(startVer) { } int GetVersion() { return m_version; } private: int m_version; }; int main(void) { Apple iphones[4] = {4, 5, 6, 7}; int latest = sizeof(iphones)/sizeof(iphones[0])-1; cout<<"iphone "<<iphones[latest].GetVersion()<<endl; return 0; }
不论是内置类型数组(包括字符数组),仍是类类型数组,都不支持直接复制与赋值操做。code
通常经过下标操做符来访问数组的元素。在用下标访问元素时,vector使用vector::size_type做为下标的类型,而数组下标的正确类型是size_t。程序员必须对数组下标的使用负责,防止缓冲区溢出(buffer overflow)错误。对象
2. 数组下标与指针
在表达式中使用数组名时,该名字会自动转换为指向数组第一个元素的指针。使用指针的算术操做(pointer arithmetic)来获取指定内容的存储地址相比下标操做更加方便。例如:
int ia[] = {0, 2, 4, 6, 8}; int last = *(ia +4); // ok: last initialized to value of ia[4] int *ip = ia; // ip points to ia[0] int *ip2 = ip+4; // ok: ip2 points to ia[4]
使用下标访问数组时,其实是对指向数组元素的指针作下标操做。只要指针指向数组元素,就能够对它进行下标操做:
int *p = &ia[2]; int j = p[1]; // ok: p[1] equivalent to *(p+1) int j = p[-2]; //ok: p[-2] equivalent to *(p-2), same to ia[0]
数组类型的变量有三个重要的限制:
动态分配的数组没必要在编译时知道其长度,一般在运行时才肯定其数组长度,与数组变量不一样,动态分配的数组将一直存在,直到程序显式释放它为止。
每个程序在执行时都占用一块内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区(free store)或堆(heap)。C语言使用一对标准库函数malloc和free在自由存储区分配存储空间,而C++语言则使用new和delete表达式实现相同的功能。
1. 动态数组的定义与初始化
动态分配数组只需指定类型和数组长度,没必要为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:
int *pia = new int[10]; // array of 10 uninitialized ints inr *pia2 = new int[10](); // 10 elements of ints array initialized to 0
new表达式须要指定指针类型以及在方括号中给出的数组维数,该维数能够是任意的复杂表达式。在动态分配数组时,若想初始化内置类型,则只能采用值初始化方式,而类类型无论有没有值初始化都会调用默认构造函数初始化。总的来讲,动态分配的数组,其数组元素只能初始化为元素类型的默认值,而不能采用初始化列表的方式提供初值。
2. 容许动态分配空数组
size_t n = get_size(); int *p = new int[n]; for(int *q = p; q != p + n; ++q) /* process the array *
上例中,即便n = 0(get_size返回0)时,程序仍能正常运行。C++虽然不容许定义长度为0的数组变量,但使用new动态建立长度为0的数组是合法的。
对数组为零的动态数组容许的操做包括:比较运算,指针加减零运算。
3. 动态空间的释放
若是再也不须要使用动态建立的数组,程序员必须显式地将其占用的存储空间返还给自由存储区。C++中释放指针所指向的数组空间:
delete [] pia;
关键字delete与指针间的空方括号是必不可少的,不然将产生内存泄漏(memory leak)。
1. 字符数组
字符数组既能够用一组由花括号括起来,逗号隔开的字符字面值进行初始化,也能够用一个字符串字面值进行初始化。需注意的是字符串字面值包含一个额外的空字符(null)用于结束字符串,例如:
char chs1[] = {‘C’, '+', '+'}; char chs2[] = {'C', '+', '+', '\0'}; char chs3[] = "C++"; // null terminator added automatically
其中,字符数组chs2和chs3值相同,而且维数都是4。
动态字符数组的定义与初始化:
char *pNewChars = new char[newSize]; // uninitialized if(pNewChars != NULL) { char *iter = pNewChars; while(iter != (pNewChars + newSize)) *iter++ = '\0'; }
2. C风格字符串(C-style character string)
实际上,C风格字符串既不能确切地归结为C语言的类型,也不能归结为C++语言的类型,能够说它是以空字符null结束的字符数组,例如上面的ch2和ch3,字符串字面值是该类型的实例。
C++语言中通常经过(const) char*类型的指针来操纵C风格字符串。须要注意的是,C风格字符串必定是以null结束的,若是发现char*的数组中不是以null结束,那么它就不是C风格字符串,这样来看,C风格字符串实际上是字符数组的一个子集。
const char* cp = "some value"; // null terminated
标准库类string
string类型支持长度可变的字符串。要使用string类对象,必须包含相关头文件:
#include <string>
表1 几种初始化string对象的方式 |
string s1; 默认构造函数,空字符串 string s2(s1); 将s2初始化为s1的一个副本 string s3("Value"); 将s3初始化为一个字符串字面值副本 string s4(n, 'c'); 将s4初始化为字符'c'的n个副本 |
表中的第三种初始化方式就是使用字符串字面值(属于C风格字符串类型)进行初始化string类对象的,但须要注意的是string类型不符合C风格字符串的特征,例如:
string str("Hi, C++"); cout<<"string size = "<<str.size()<<endl; // size = 7
string对象会自动将字符串字面值末尾null结束符去掉,所以获得的string对象size = 7。但string类提供了一个名为c_str的成员函数来转换获得C风格字符串:
const char *cStr = str.c_str(); //ok
注意:c_str函数返回的指针是指向const char类型的数组,须要赋给const char*类型
C风格字符串的标准库函数
C语言标准库提供了一系列处理C风格字符串的库函数,须要包含C头文件:
#include <cstring>
cstring是string.h头文件的C++版本,string.h文件则是C语言的标准库。
表2 C风格字符串的标准库函数 |
strlen(s) 返回s的长度,不包括字符串结束符null strcmp(s1, s2) 比较两个字符串s1和s2是否相同,相等返回0;若s1大于s2返回正数,若s1小于s2返回负数 strcat(s1, s2) 将字符串s2链接到s1,并返回s1 strcpy(s1, s2) 将s2复制给s1,并返回s1 strncat(s1, s2, n) 将s2的前n个字符链接到s1后面,并返回s1 strncpy(s1, s2, n) 将s2的前n个字符复制给s1,并返回s1 |
在使用这些标准库函数时,必须保证传递的指针具备非零值,而且指向以null结束的字符数组中的第一个元素。对于会修改传入字符串的标准库函数,程序员必须确保目标字符串必须足够大,不然会产生严重错误。
正是存在不少缺陷,C++中推荐尽可能使用标准库类型string来代替这些操做,提升安全性和效率。
Reference book: C++ primer