3.8 数组的分配和访问
1.基本原则
数组分配时会分配一段连续的内存地址,访问时根据该数组元素的大小来使用偏移访问。
例如一个int数组的头指针是x,那么x[3]就是x+3*4,4是int的字节数。
2.指针运算
1)c语言中可以对指针直接进行运算,运算时会根据该指针引用类型的大小进行缩放。
例如一个指向int的指针++就是将这个指针的地址+4
2)* 和 &运算
*相当于取该指针指向的元素,&相当于取该元素的地址,相当于创建了一个临时指针
3.理解指针和数组
考虑这样一道题,前面的都很简单,主要在于
第一,A1和A2是否会造成null pointer reference,答案是不会的,A1在声明时声明了一个三个int的数组,调用A1就是调用数组的头指针,这个数组已经在内存空间里开辟了,是有实际地址的,所以不会造成null pointer reference,A2是声明了一个int的指针,已经为这个指针申请了大小为8的空间,所以也不会有null pointer reference。
第二,*A1和*A2,首先*A1就是对A1这个地址取他指向的值,而这个值就是A[0],其次,它会导致null pointer reference吗,不会,原因和上面一样。其次*A2,他是对A2这个指针取他指向的值,也就是某个int类型变量,但是在声明这个的时候并没有让他指向某个int值,所以有可能会导致null pointer reference。
上面的比较简单,下面考虑这个题
第一个是一样的,只看**A1,这相当于是拿*A1指向的东西,可是*A1不是指针,他是一个int,所以这个语句都不能通过编译。
第二个要理解是什么,需要理解c语言指针的阅读顺序,从里到外读,先读*A2,声明了一个指针,然后读[3],这是个指针数组,在读int,这是个int型的指针的数组,也就是数组里放了三个指向int的指针,明白了这个就可以看后面的了,*A2就是这个指针数组的头指针也就是A2[0],**A2就是A2[0]这个指针所指向的int型变量。
第三个我目前还没弄明白。
上课还补充了一个,int (*A4[3]),这个跟第二个是一样的。
4.嵌套的数组
有两种嵌套数组,一种是直接声明一个二维数组,a[3][5],那么在访问的时候就会根据定量的行和列偏移来计算位置。第二种是声明三个一维数组,然后在声明一个三个元素的数组,把刚刚声明的三个数组装进去。这样在c语言层面上是一样的,但在机器层面是不一样的,访问时要根据这三个头元素来分别进行偏移访问,不能通过一个头元素来完成所有的偏移。
5.定长数组
在进行定长数组的一些计算时,编译器会进行一些优化,下面的例子很好的展示了这个优化
3.9 异质的数据结构
1.struct
1)struct跟数组很类似,指向结构体的指针会指向这个结构体的第一个字节,然后通过偏移来实现访问。
2)数据对齐的问题
很多机器因为一些硬件层面的原因,比如一次性读固定个字节,那么每个元素的起始地址就必须是这个固定数的倍数,如果不这样可能会产生些别的多余的计算,所以编译器会自动将数据对齐,以提高效率。
考虑下面这个结构体,不用的放置方式会导致占用空间的变化,所以可以以大数据在前的贪心算法来排列结构体,以节省空间。
3)union结构
这个没看明白例子,以后有机会再看。