关于C++数组的几点讨论
- 数组名为何物?
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[], int size)等价于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。