数组
定义
在Java中,通过int[] a
这样的写法定义数组类型就非常简单明了,但是在C++中偏偏不能这么写,还必须写成int a[]
,将括号放在变量后。建议是,按照Java的数组写法理解数组的逻辑,但是书写的时候转换成C++的写法。
按照Java的方式理解数组,很多问题迎刃而解。比如说,在函数传参时传入数组,一定要在参数列表中指定数组除了第一维之外的维数,例如可以写成void f(int a[][2])
但是不能写成void f(int a[3][])
。这是因为int a[][2]
实际上是(int[2])[] a
,即a是一个数组,数组的每个单元是一个长度为2的整数数组。编译器必须知道数组内每个元素的空间大小,以便能够正确计算偏移量;编译器不需要知道数组自身的长度,因此如果函数中需要用到数组自身长度,还需要再传入一个int型的参数进行告知。
另外,在定义数组的时候可以缺省第一维,但是不能不指定其他维度的具体长度(如果有更高的维度),也是这个道理。(但是如果没有用大括号初始化也没有用new,还是要指定数组总长度的,以便在栈中分配空间)
本质
在C++中,数组的本质是指向第一个元素的、类型为“数组中每个单元”类型的指针。除了在栈中定义数组,在定义它的环境中会保留一部分数组的属性,这时如果使用类似sizeof()
这样的函数可以获得整个数组所占用的总空间。但是,一旦数组经过类似传参这样的操作,会马上原形毕露,实实在在就是指针,丢失除了所指地址之外的任何属性。
请看下面的例子:
#include <iostream>
using namespace std;
void f(int a[]){
cout<<"f: "<<sizeof(a)<<endl;
}
void g(char c[]){
cout<<"g: "<<sizeof(c)<<endl;
cout<<"g: "<<c<<endl;
//幸运的是,cout对字符数组(字符指针)的输出进行了特殊处理,能够正确打印其中储存的字符串
}
int main(){
int a[]={0,1,2,3,4,5};
char c[]="abcde";
int* b=new int[10];
char *chars=c;
cout<<sizeof(a)<<endl; // 输出24,每个整数4字节,一共6个数
cout<<sizeof(c)<<endl; // 输出6,每个字符1字节,一共6字符(别忘了\0)
f(a); // 输出8(或者4或者别的),这个数事实上是int*类型占用空间大小
g(c); // 和上面输出一致,事实上是char*占用空间大小
g(chars); //和上面完全一致。这里证明在传参时,数组完全被当做指针处理。
// 传参之后丢失除了地址之外的一切性质。
}
因此,这也是多维数组能够升降维的关键(这部分放在指针中)。
结构体
不展开,和类基本一致,可以包含若干成员变量和方法。
唯一不同的是,结构体中的成员变量和方法不声明的情况下默认public,而类中默认private。
联合体 Union
联合体中可以定义多个成员变量,但是,里面的所有成员变量都共用同一段空间。一个联合体所占的空间取决于其成员变量中占用最大空间的那个。而且所有变量的开始地址都是对齐到联合体的开始地址的。
尽管联合体中的变量使用同一个空间,但是访问其中变量值的时候还是要用.的方式来访问,这样编译器才知道要把这段空间中的内容作为什么类型来解读。
举例:
#include <iostream>
using namespace std;
void checkCPU()
{
union MyUnion{
int a;
char c;
}test{};
test.a = 1;
if (test.c == 1)
cout << "little endian" <<endl;
else cout << "big endian" <<endl;
}
int main()
{
checkCPU();
return 0;
}
很经典的测试大小端的一段代码。放入一个整数1,结构体的内容为31个0后接1个1。这时尝试读取其中储存的字符(一个字节)。如果读到的字符还是1,说明内存中从低地址到高地址存的内容为01 00 00 00,字符为01,读整数时低地址在前高地址在后,读整数时低字节内容读到低位中,高字节内容读到高位中,是小端;如果读到的字符是0,说明内存中从低到高地址存的内容是00 00 00 01,字符为00,读整数时高地址在前低地址在后,低字节内容读入但是表示高位,高字节内容后读入但是表示低位,为大端。请注意不管是字符还是整数,开头地址都是联合体最低的那个字节地址。
Cpp内存中存储都是按照低地址到高地址的顺序存储的。联合体里面所有可能的类型会按低地址的开始位置对齐。
最后要补充的是,在栈中定义的数组变量、结构体变量或者联合体变量都能通过大括号的方式初始化。例如:
#include <iostream>
using namespace std;
struct test{
int a;
char b;
};
union gest{
int a;
char b;
};
int main()
{
char chars[]={'a','b','c'};//数组初始化
int nums[][2]={{1,2},{3,4}};//多维数组初始化
test t={1,'h'};//结构体初始化
gest g={4};//联合体初始化
cout<<t.a<<' '<<t.b<<endl; //1 h
cout<<g.a<<' '<<int(g.b)<<endl;//1 1
}