C++基础知识学习:数组

1、数组

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]

2、动态数组

数组类型的变量有三个重要的限制:

  • 数组长度固定不变
  • 在编译时必须知道其长度
  • 数组只在定义它的块语句内存在

动态分配的数组没必要在编译时知道其长度,一般在运行时才肯定其数组长度,与数组变量不一样,动态分配的数组将一直存在,直到程序显式释放它为止。

每个程序在执行时都占用一块内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区(free store)或堆(heap)。C语言使用一对标准库函数malloc和free在自由存储区分配存储空间,而C++语言则使用newdelete表达式实现相同的功能。

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)。

3、字符数组与C风格字符串

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

相关文章
相关标签/搜索