IT点滴

我不去想是否能够成功 既然选择了远方 便只顾风雨兼程
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

关于C++数组的几点讨论

Posted on 2018-11-09 14:33  Ady Lee  阅读(204)  评论(0编辑  收藏  举报
  • 数组名为何物?
复制代码
int main()
{
    int number[] = { 1, 2, 3, 4, 5 };
    int *pnumber = number;
    cout << sizeof(number) << endl;
    cout << sizeof(pnumber) << endl;

    system("pause");
    return 0;
}
复制代码

32位系统中,指针大小是4字节,输出结果:

所以number不是指针。

但数组名又可以直接赋值给指针:

数组名不能自加自减:

数组名是一个指针常量

(1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;

(2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;

(3)指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址!

  • C++数组做参数为什么一定要指定数组的长度?

看下面的这个例子:

复制代码
#include <iostream>
using namespace std;
int max(int a[], int size);//size是数组的大小

int main()
{
    int number[] = { 2, 45, 12, 6, 23, 98, 13, 3 };
    cout << max(number, sizeof(number) / sizeof(int)) << endl;
    system("pause");
    return 0;
}


int max(int a[], int size)
{
    int max = 0;
    for (int i = 0; i<size; i++)
    {
        if (a[i]>max)
            max = a[i];
    }
    return max;
}
复制代码

在定义完数组“int number[] = { 2, 45, 12, 6, 23, 98, 13, 3 };”之后,如果有一句:“cout << number[8] << endl;”并不会判断出来是越界,而是输出:

数组是一个首地址,至于后面数组在哪里结束,C++的函数传递机制并不负责。

再来看下内存:

所谓数组只是把数据按序存放在内存中,程序只是根据数组的首地址向后依次取出数据,而当你大于数组长度时,程序自己也并不知道,而是依旧把后边的地址中的内容(“cccccccc”)当做数据取出。

当数组作为参数传递到子函数中去时,仅仅会被子函数解析为一个地址:

如果输出a的值,就是:

令举一例:

复制代码
#include <iostream>
using namespace std;

void testArrayArg(int a[])
{
    cout << endl;

    cout << "in func..." << endl;
    cout << "array address: " << a << endl;
    cout << "array size: " << sizeof(a) << endl;
    cout << "array element count: " << sizeof(a) / sizeof(a[0]) << endl;

    cout << "changing the 4th element's value to 10." << endl;
    a[3] = 10;//以0x0042F980为首地址,修改第三个数据的值
}

int main()
{
    int number[] = { 1, 2, 3, 4, 5 };

    cout << "in main..." << endl;
    cout << "array address: " << number << endl;
    cout << "array size: " << sizeof(number) << endl;
    cout << "array element count: " << sizeof(number) / sizeof(number[0]) << endl;

    testArrayArg(number);

    cout << endl << "the 4th element's value: " << number[3] << endl;
    system("pause");
    return 0;
}
复制代码

输出结果:

其实,修改a[3]的值,number[3]的值也随之改变,因为数组a和数组number都是以相同地址为首地址,修改的是同一地址中的内容,number被解析为数组,a被解析为指针,但二者都可以向后依次解析数据。

而且就子函数func输出的数据而言,a只被解析为一个元素,大小4字节,即数组number传递到a时,a只是一个指针,一个指针并不知道从自身开始往后有多少个数据。因此,如果在接受数组参数的函数中访问数组的各个元素,需在定义数组的域范围将数组大小作为另一辅助参数传递,以免以指针作为数组使用时访问越界。

我们得出结论:数组做参数时,退化为指针,以数组作为参数时,要同时传入数组大小,否则只知道这个数组的起始地址而不知道它的长度。即这里max(int a[], intsize)等价于max(int*a, int size)也等价于max(int a[10], int size),反正只是传递进去一个指针,不必在意如何“修饰”a。

参考:http://www.cnblogs.com/viviwind/archive/2012/08/16/2642535.html,这里边对多维数组也进行了简单说明。

  • 将数组作为参数时声明为数组的引用:

前面我们讨论了,当数组作为参数传递给子函数时,数组退化为指针,子函数不知道传递给他的数组的长度,编译器也不知道,编译器对参数类型进行检查时,也不会检查数组的长度。但当参数是一个数组类型的引用时,无论是声明还是定义都必须指明参数数组的大小,否则会提示错误:

    

同样,如果你企图传入一个大小并不为8的数组时,也会提示错误:

我们使用数组的引用作为参数的目的,就是避免数组作为参数传递给子函数时会退化为指针

修改一下上一例子:

复制代码
#include <iostream>
using namespace std;


void testArrayArg1(int a[])
{
    cout << endl;

    cout << "in func1..." << endl;
    cout << "array address: " << a << endl;
    cout << "array size: " << sizeof(a) << endl;
    cout << "array element count: " << sizeof(a) / sizeof(a[0]) << endl;
}

void testArrayArg2(int (&a)[5])
{
    cout << endl;

    cout << "in func2..." << endl;
    cout << "array address: " << a << endl;
    cout << "array size: " << sizeof(a) << endl;
    cout << "array element count: " << sizeof(a) / sizeof(a[0]) << endl;

}

int main()
{
    int number[] = { 1, 2, 3, 4, 5 };

    cout << "in main..." << endl;
    cout << "array address: " << number << endl;
    cout << "array size: " << sizeof(number) << endl;
    cout << "array element count: " << sizeof(number) / sizeof(number[0]) << endl;

    testArrayArg1(number);
    testArrayArg2(number);

    system("pause");
    return 0;
}
复制代码

运行后我们发现,main函数和func2的结果是相同的:

func1中的a被解释为指针:

而func2中的a仍旧被解析为数组:

参考:http://blog.csdn.net/jiangxinyu/article/details/7767065

  • 数组名和数组的地址:
复制代码
int main()
{
    int number[] = { 1, 2, 3, 4, 5 };
    cout << number << endl;
    cout << &number << endl;
    cout << number + 1 << endl;
    cout << &number + 1 << endl;
    system("pause");
    return 0;
}
复制代码

运行结果:

a是数组名,代表数组第一个元素的地址;&a是整个数组的地址。二者输出的值是相同的,都是0x0032FB88,但是二者并不同,前者是从一个元素的角度来看的,而后者是从整个数组的角度来看的。所以a+1仅仅是加了一个元素的大小,而&a+1加了一个数组的大小。其实,&a+1、&a+2这种形式更常见于对二维数组的操作,因为二维数组就是数据成员是数组的数组,所以对其中一个元素的操作就是对一个数组的操作。

  • 如何给字符数组赋值?

字符串可以直接赋值,然而字符数组不能这样直接赋值:

原因就是因为数组名是一个指针常量,已经指向了一个已开辟30字节的栈空间,又让它指向字符串,因此会报错:

在C中我们曾这样使用:

但是考虑到安全性,现在都建议使用strcpy_s:

注意,数组只有在定义的时候才能初始化,否则必须通过其它方法赋值,比如循环。因此,下面这样的操作是错误的:

可以这样赋值,在定义时直接用字符串赋值:

对数组中字符逐个赋值:

也可以省略数组的大小:

或者利用上面提到的strcpy_s。