C++数组、动态数组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36748278/article/details/79948293

先了解一下数组的结构,然后接着了解动态数组的结构。

数组

数组:
(1)数组大小固定。
(2)数组是一种复合类型
(3)存放类型相同的对象的容器,需要通过所在位置访问这些对象。
(4)数组的元素应该为对象,因此不存在引用的数组
(5)维度:数组中元素的个数,必须大于0,编译的时候维度应该是已知的,也就是说,维度必须是一个常量表达式
(6)不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
(7)在使用数组下标的时候,通常将其定义为size_t类型。size_t是一种机器相关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小。
(8)通过数组名字和数组中首元素的地址都能得到指向首元素的指针。

//不允许拷贝和赋值
#include <iostream>
using namespace std;

int main(){
    int a[] = {0,1,2};
    //int a2[] = a;     //错误:不允许使用一个数组初始化另一个数组 
    //a2 =a;            //错误:不能把一个数组直接赋值给另一个数组
    return 0;
}

//数组的维度
#include <iostream>
using namespace std;

int main(){
    unsigned uint = 10;             //不是常量表达式
    constexpr unsigned cuint = 10;  //常量表达式 

    //string str[uint];             //编译报错:uint不是常量表达式
    string str[cuint];              //含有10个整数的数组 
    string *cstr[cunit];            //含有10个整型指针的数组

    return 0;
}


复杂数组定义的解读方法:由名字开始由内向外解读
(1)默认情况下,类型修饰符从右向左依次绑定,对于ptrs:首先我们定义的是一个大小为10的数组,它的名字是prts,然后知道数组中存放的是指向int的指针。
(2)但是对于parray来说,从左到右理解,即由内向外阅读就更合适。首先是圆括号括起来的部分,(*parray)意味着parray是一个指针,接下来观察右边(*parray)[10],可以知道parray是个指向大小为10的数组的指针,然后在观察左边,int (*parray)[10],知道数组的元素是int。这样就知道parray是一个指针,它指向一个int数组,数组中包含10个元素。
(3)int *(&array)[10] = ptrs;首先知道array是个引用,然后观察右边知道引用的对象是一个大小为10的数组,然后观察左边,数组的元素是一个指向int的指针。这样,就得出了array就是一个含有10个int型指针的数组的引用。

//复杂数组的声明
#include <iostream>
using namespace std;

int main(){
    int *ptrs[10];          //含有10个整形指针的数组

    int arr[10];
    int (*parray)[10] = &arr;   //parray 指向 一个含有10个整数的数组 
    int (&arrRef)[10] = arr;    //arrRef 引用 一个含有10个整数的数组 
    int *(&array)[10] = ptrs;   //
    return 0;
}

//数组的初始化
#include <iostream>
using namespace std;

int main(){
    const unsigned sz = 3;  

    int a1[3] = {0,1,2};            //含有3各元素的数组,元素分别是0,1,2 
    int a2[] = {0,1,2};             //维度是3的数组 
    int a3[5] = {0,1,2};            //等价于a3[] = {0,1,2,0,0} 
    string a4[3] = {"i","and"};     //等价于a4[] = {"i","and",""} 

    //int a5[2] = {0,1,2};          //错误:初始值太多 

    return 0;
}


字符数组有一种额外的初始化形式,可以通过字符串字面量对此类数组初始化 ,使用这种方式需要注意字符串字面值的结尾处还有一个空字符,这个空字符也会像字符串的其它字符一样被拷贝到字符数组中去。

//字符数组的初始化
#include <iostream>
using namespace std;

int main(){
    char a1[] = {'c','+','+'};      //列表初始化,没有空字符串 
    char a2[] = "c++";              //自动添加表示字符串结束的空字符
    //const char a4[5] = "Hello";   //错误。"Hello"看起来只有5个字符,但是数组大小必须是6,其中5个位置存放字面值的内容,另外一个存放结尾处的空字符 

    return 0;
}



数组和指针
(1)很多用数组名字的地方,编译器会自动地将其替换为一个指向数组首元素的指针
(2)指针加上一个整数得到的结果还是一个指针
(3)

#include <iostream>
using namespace std;

int main(){
    string nums[] = {"i","love","you","haha"};
    string *p = &nums[0];       //p指向nums的第一个元素
    string *p2 = nums;          //等价于string *p2 = &nums[0] ,此时p2指向nums[0] 
    ++p2;                       //此时p2指向nums[1]; 

    string *last = &nums[4];    //得到数组尾元素之后那个并不存在的元素的地址。尾后指针不可以执行解引用和递增操作
    return 0;
}
#include <iostream>
#include <iterator>
using namespace std;

int main(){
    int a[] = {0,2,4,5,7,8};

    int s = *a;         //a[0]的值,即为0
    int s1 = *(a + 3);  //a[4]的值,即为5
    int s2 = *a + 3;    //a[0] + 3的值,即为3

    int *p = &a[2];     //p指向索引为2的元素
    int val = p[1];     //p[1]等价于 *(p+1),也就是a[3]表示的那个元素 
    int val1 = p[-2];   //p[-2]等价于 *(p-2),也就是a[0]表示的那个元素 

    return 0;
}



数组的遍历

#include <iostream>
#include <iterator>
using namespace std;

int main(){
    string nums[] = {"i","love","you"};

    //通过指针遍历 
    string *p = nums;          //此时p指向nums[0],数组首元素的指针 
    string *last = &nums[3];   //得到数组尾元素之后那个并不存在的元素的地址 
    for(;p != last;++p){
        cout << *p << endl;
    }

    //通过标准库函数begin和end遍历
    int *begin = begin(nums);       //指向nums首元素的指针 
    int *end = end(nums);           //指向nums尾元素的下一个位置的指针
    while(begin != end){
        cout << *begin << endl;
        ++begin;
    } 

    return 0;
}

多维数组

#include <iostream>
using namespace std;

int main(){
    int a[3][4];            //大小为3的数组。每个元素是含有4个整数的数组 
    int (*p)[4] = a;        //p指向含有4个整数的数组 

    for(auto &row:a){
        for(auto &col:row){
            cout << *col << " ";
        }
        cout << endl;
    }

    for (int (*p)[4] = begin(a);p != end(a);p++){
        for (int *q = begin(*p);q != end(*p); q++){
            cout << *q <<endl;
        }
    }

    return 0;
}

动态数组

new和delete运算符都是一次分配/释放一个对象。
但是像vector和string都是在连续内存中保存它们的元素,因此,当容器需要重新分配内存时,必须一次性为很多元素分配内存。

通过new来分配一个对象数组
分配一个数组会得到一个元素类型的指针。虽然我们称new T[]分配的内存为“动态数组”,当用new分配一个数组时,我们并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end。也不能用for语句来处理动态数组中的元素。

#include <iostream>
#include <list>
using namespace std;

int main(){
    int *pa = new int[10];              //10个未初始化的int ,pa指向第一个int元素
    int *pa2 = new int[10]();           //10个值初始化为0的int
    int *pa3 = new int[10]{0,1,2,3,4,5,6,7,8,9};    //使用初始化器初始化 

    string *spa = new string[10];       //10个空string 
    string *spa2 = new string[10]();    //10个空string
    string *spa3 = new string[10]{"a","b","c",string(3,'x')};   //前四个使用初始化器初始化,后面的就默认进行值初始化 

    return 0;
}

释放动态数组
通过delete []a,销毁a指向的数组中的元素,并释放对应的内存。数组中的元素按照逆序进行销毁。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页