不一样的C语言-当sizeof遇上数组名

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

当sizeof遇上数组名

一、 sizeof是何方神圣

说到sizeof,首先必须知道的一点:sizeof不是函数,sizeof是操作符。sizeof的作用就是,计算一个对象或类型所占的内存字节数。

1.1 sizeof的语法

sizeof():
这也正是为什么sizeof会有那么多人误解其为函数。括号里面可以是一个数据对象,也可以是一个数据类型。对于有括号的这种语法来说,你大可以不用理会括号里面是一个数据类型还是一个数据对象。
sizeof object:
初次见面,以为是语法错误,其实这也是sizeof的一种用法,计算数据对象所占的内存字节数。如果计算的是一个数据类型,那么请用上面的括号形式。如此看来,那么多程序员使用()的语法形式是有道理的,因为放之四海皆准。

1.2 sizeof如何计算大小的

sizeof既然那么伟大,那么他是如何伟大的,什么时候做了这个伟大的事情呢?我们知道,当我们定义一个变量的时候,我们就获得了一个可以供我们使用的内存空间,那么计算分配内存大小的事情又是谁做的呢?如果你对程序的编译和运行稍作了解,你就会知道,这个事情是编译器帮我们干的。编译器在编译的时候,告知了系统(这么说其实不太准确,暂且这么理解),运行这个程序的时候,需要如何分配内存。那么sizeof也正是发生在这个时期。编译的时候,编译器就能够知道我们sizeof的类型或者对象所需要的内存大小。

1.3 为什么需要sizeof来计算大小

如果你学习过JAVA,那么你会发现,JAVA中根本不存在sizeof操作符。难道是因为JAVA不够完善吗?其实不然。JAVA之所以没有sizeof操作符,也正如JAVA设计者所言:JAVA不需要sizeof。JAVA的运行时需要JVM的,不同的操作系统都有对应的JVM,在JVM中保证了所有类型的大小一致,不管你运行在32位还是64位的机器上,数据类型的大小都是已知的,这也正是JAVA可移植性能够实现的最根本的原因。回到话题来,为什么C/C++需要sizeof?显然,对于一个合格的C程序员,我们应该知道,我们所写的程序运行在不同结构的机器上的时候,类型的大小有所区别。当我们需要知道一个类型的大小才能继续写程序的时候,我们就需要sizeof。这就是为什么我们需要sizeof。

二、 数组并没有那么简单

数组和指针是C语言中的重头戏,那么数组是什么数据类型呢?按照其他书籍的介绍,数组是复合类型(与基本数据类型结合构成的类型)。数组是同一类型的数据的集合,在内存中的表现为一串连续的内存,内存的大小为单个数据类型的大小与数据量的乘积。在我个人看来,数组更应该称为数据结构。数据结构中的顺序表,其实现的核心就是数组。由此可见,数组更像是一种C语言预先定义的数据结构。

2.1 数组的语法

数据类型 数组名[数据个数]
倘若我们在定义的时候就初始化,那么数组中第一个维度的数据大小可以不写。

2.2 数组名到底是什么

数组名是一段内存空间的名字,其指向了这段内存空间。我们讲到了sizeof,那么sizeof(数组名)计算出来的,理所应当计算的就是数组名指向的这段内存空间的字节数,事实也正是如此。但是,当我们对数组名继续深究下去,我们就会开始对sizeof(数组名)疑惑了。
int arr[3] = {0,1,2};
printf(“%p”,arr);

上面的例子,打印的将会是数组首元素的地址。由此我们可以推测:arr是一个指针。我们这么推测是有理由的,因为指针才能存储一个地址。但是倘若我们想现在下定论,就需要考虑sizeof(arr)。这时候你再来回答,sizeof(arr)的大小是多少。两种答案:4或者12。答案是4的显示,我们把arr当做一个指针,指针的内存在所占的字节数为4;12则是把数组名看做一种数据结构,这个数据结构的大小为12字节。正确答案大家知道是12。到这里我们的疑惑就更深了,既然编译器认为arr不是指针,但是arr在打印地址的时候,又保存了一个地址。这就是我们需要说的:数组名是一个类似于指针的数据结构变量名。在普通情况下,我们可以把数组名当做指针来使用。继续看下面的一个例子:
int arr[3] = {0,1,2};
int arr2[3] = {3,4,5};
arr = arr2;

上面的代码我们想完成的事情是将arr2数组赋值给arr数组,但是编译的时候是错误的,因为arr在表现为指针属性的时候,实际上是一个常量指针。我们不会对一个已经初始化了的变量再次赋值。

2.3 数组作为函数形参

当数组作为函数形参的时候,数组名则沦为指针,既然是一个指针,我们就可以对其自增、自减和修改。在函数体内,数组名仅仅作为一个数组的指针。我们知道了他是指针,只要我们搞清楚这个指针实际指向的数据类型,那么对于指针的使用就显得简单了。

三、 sizeof与数组

int arr[2][2] = {
{0,1},
{2,3}
};
printf(“%d,”,sizeof(arr));
printf(“%d,”,sizeof(arr[0]));
printf(“%d\n”,sizeof(arr[0][0]));

三个printf将会打印什么?答案:16,8,4

3.1 sizeof(arr)

弄清楚答案的缘由我们先了解一下多维数组。
C/C++中,多维数组实际上是一位数组。多维数组在分配内存的时候,是分配一段完整的连续的内存空间。这么说可能理解不深刻。我们先来了解一下JAVA中的多维数组,就以二维数组来看。JAVA中的二维数组在分配空间的时候,首先会分配一个大小为2的数组,其保存的是2个一维数组的起始地址。这两个一维数组在内存上并不一定是连续的。再看看C中的数组,就能理解其完整的连续的意思。
正是因为如此,所以sizeof(arr)才会打印16。因为sizeof计算的是数组名对应的内存空间的大小,不管维度大小。

3.2 sizeof(arr[0])

接下来我们看一下sizeof(arr[0])。如果外面没有sizeof操作符,arr[0]在此处如果换成指针来看待,其运算如下:(arr + 0 * 2),其指向的依然是数组的首行首元素的地址。但是在sizeof操作符下,arr[0]显然不能够当做指针来看待,这时应该理解为二维数组的一维数组(逻辑上是如此,实际上多维数组还是一位数组)。arr[0]指向的是第一行的一位数组,我们可以理解为arr[0]就是一个数组名,其内存空间为arr数组的前2个元素对应的内存空间,我们sizeof,得到的结果就应该为8。

3.3 sizeof(arr[0][0])

最后来看sizeof(arr[0][0]),arr[0][0]表示访问的是二维数组的首行一维数组的首元素,其变量就是一个int类型,所以结果为4。
由此看来,数组名与sizeof操作符搭配的时候,其表现也并不简单,其依然还是保留了数组名作为一种数据结构的特性。



猜你喜欢

转载自blog.csdn.net/water_3700348/article/details/78118760