在C语言中,数组有着极其重要的地位,它不仅是一种存放同一类型数据的集合,而且和指针也有着一定的关联。在这其中,数组名的含义与使用也是有一定学问的,总体来说可以概括为以下两点:
1. sizaeof()和取地址&后面单独跟数组名,此时数组名代表整个数组;
2. 除以上两种情况外,数组名表示数组首元素的地址;
接下来我将通过展示一些实例来帮助大家更好地理解和使用数组名。
1 一维整型数组的相关内容
第一组代码如下:
对于上面的5行代码,我们逐个分析:
1)因为a是一个整型的一维数组,且a单独放在sizeof()中,此时数组名a代表整个数组,sizeof(a)求的是整个数组的大小,故打印出来的结果为16。
2)a+0作为一个整体放在sizeof()中,此时a表示的是数组首元素的地址,因此sizeof()求的是数组首元素地址的大小。而对于地址的大小,如果是32位处理器,其内存大小位四个字节4个;如果是64位处理器,其内存大小为8个字节。故最终打印出的结果为4/8。
3)*a放在sizef()中,a表示首元素的地址,对a进行解引用操作,所以sizeof()求的是数组第一个元素的大小,故打印结果为4。
4)a+1作为一个整体放在sizeof()中,此时a表示的是数组首元素的地址,因此sizeof()求的是数组第二个元素地址的大小,故最终打印出的结果为4/8。
5)a[1]表示数组的第二个元素,sizeof()求的是数组第二个元素的大小,故打印结果为4。
实际运行结果如下:
第二组代码如下:
对于后续的结果,之后的分析内容统一以32位处理器来看待,不再重复赘述。
分析如下:
1)&a放在sizeof()中,由于a单独跟在取地址&的后面,此时a表示的是整个数组的大小,而sizeof()求的是指向整个数组大小的地址的内存的大小,其本质上仍然是一个地址,故打印结果为4。
2) &a+1放在sizeof()中,由于a单独跟在取地址&的后面,此时a表示的是整个数组的大小,&a+1表示整个数组大小的地址往后移一个数组大小的位置,sizeof()仍然求的是一个地址,故打印结果为4。
3)&a[0]表示求数组首元素的地址,sizeof()求的是数组首元素的地址的内存的大小,故打印结果为4。
4)&a[0]+1表示数组首元素的地址再往后移一个位置,即数组第二个元素的地址,sizeof()求的仍然是一个地址,故打印结果为4。
实际运行结果如下:
2 一维字符数组的相关内容
第一组代码如下:
首先,关于strlen()函数,它的使用参数是一个字符指针,返回的结果是从参数地址起往后至字符'\0'止之间的字符的数量,不包含字符‘\0’,也就是字符串的大小。
分析如下:
1)arr表示字符数组首元素的地址,由于我们给出的内容并没有包含‘\0’,strlen()函数会从数组首元素地址一直往后找,直到找到字符‘\0’,因此strlen()返回的是一个随机值,故打印结果也是一个随机值。
2)arr+0放在strlen()中,arr表示数组首元素地址,strlen()也是从数组首元素地址开始,故打印结果为随机值。
3)*arr表示数组的首元素,由于此时传给strlen()的并不是一个地址,因此编译器会报错。
4)arr[1]表示数组的第二个元素,此时传给strlen()的并不是一个地址,因此编译器会报错。
5)&arr放在strlen()中,由于arr单独跟在取地址&的后面,arr表示整个字符数组,strlen()从整个字符数组的地址开始,其地址形式与数组首元素的地址一致,但指向的内容大小不一样。而对于strlen()函数而言,当地址传过去是,会将其强制转换成字符指针,故打印结果为同一个随机值。
6)&arr+1放在strlen()中,由于arr单独跟在取地址&的后面,arr表示整个字符数组,strlen()从整个字符数组的地址往后移一个数组大小的地址位置开始,即6个字节的大小,故打印结果为随机值-6。
7)&arr[0]+1放在strlen()中,&arr[0]表示字符数组的首元素,strlen()从字符数组首元素的地址往后移一个字节大小的地址位置开始,故打印结果为随机值-1。
排除两行错误的代码,实际运行结果如下:
第二组代码如下:
这一组代码主要是关于字符数组与sizeof()之间的结合,其分析逻辑与整型数组相似,具体分析如下:
1)arr是一个字符数组,且arr单独放在sizeof()中,此时数组名arr代表整个数组,sizeof(a)求的是整个数组的大小,故打印出来的结果为6。
2)arr+0作为一个整体放在sizeof()中,此时arr表示的是数组首元素的地址,因此sizeof()求的是数组首元素地址的大小,故最终打印出的结果为4。
3)*arr放在sizef()中,arr表示首元素的地址,对arr进行解引用操作,所以sizeof()求的是数组第一个元素的大小,故打印结果为1。
4)a[1]表示数组的第二个元素,sizeof()求的是数组第二个元素的大小,故打印结果为1。
5)&arr放在sizeof()中,由于arr单独跟在取地址&的后面,此时arr表示的是整个数组的大小,而sizeof()求的是指向整个数组大小的地址的内存的大小,故打印结果为4。
6)&arr+1放在sizeof()中,由于arr单独跟在取地址&的后面,此时arr表示的是整个数组的大小,&a+1表示整个数组大小的地址往后移一个数组大小的位置,sizeof()仍然求的是一个地址,故打印结果为4。
7)&arr[0]+1表示数组首元素的地址再往后移一个位置,即数组第二个元素的地址,sizeof()求的仍然是一个地址,故打印结果为4。
实际的运行结果如下:
第三组代码如下:
对于这组代码,其与第一组的主要区别在于以字符串的形式进行初始化,这样的方式使的数组的最后一位会存放一个字符‘\0’,因此,strlen()的计算会有一个明确的值。而其分析逻辑与第一组基本无异,具体如下:
1)arr表示字符数组首元素的地址,strlen()函数会返回参数地址起往后至字符'\0'止之间的字符的数量,故打印结果为6。
2)arr+0放在strlen()中,arr表示数组首元素地址,strlen()也是从数组首元素地址开始,故打印结果为6。
3)*arr表示数组的首元素,由于此时传给strlen()的并不是一个地址,因此编译器会报错。
4)arr[1]表示数组的第二个元素,此时传给strlen()的并不是一个地址,因此编译器会报错。
5)&arr放在strlen()中,由于arr单独跟在取地址&的后面,arr表示整个字符数组,strlen()从整个字符数组的地址开始,其地址形式与数组首元素的地址一致,但指向的内容大小不一样。而对于strlen()函数而言,当地址传过去时,会将其强制转换成字符指针,即数组首元素地址,故打印结果为6。
6)&arr+1放在strlen()中,由于arr单独跟在取地址&的后面,arr表示整个字符数组,strlen()从整个字符数组的地址往后移一个数组大小的地址位置开始,即7个字节的大小,此时的地址及其后面位置的内容是未知的,故打印结果为随机值。
7)&arr[0]+1放在strlen()中,arr[0]表示字符数组的首元素,&arr[0]则表示首元素的地址,strlen()从字符数组首元素的地址往后移一个字节大小的地址位置开始,即数组第二个元素的地址,故打印结果为5。
排除两行错误的代码,实际运行结果如下:
第四组代码:
这组代码与第二组代码基本一致,只是数组的大小由6变成7,具体分析就不在赘述了,其实际运行结果如下:
第五组代码:
这组代码采用指针的形式来指向一个字符串,指针本身表示第一个元素的地址,除去数组名单独跟在sizeof()和取地址&后面的含义,其它时候字符指针变量p与数组名之间是完全等效的。换句话说,p再任何时候都表示首元素的地址。具体的分析逻辑与第三组代码是一致的,如下:
1)p表示字符数组首元素的地址,strlen()函数会返回参数地址起往后至字符'\0'止之间的字符的数量,故打印结果为6。
2)p+1放在strlen()中,表示第二个元素地址,故打印结果为5。
3)*p表示首元素,由于此时传给strlen()的并不是一个地址,因此编译器会报错。
4)p[0]实际等价于*(p+0),表示第二个元素,此时传给strlen()的并不是一个地址,因此编译器会报错。
5)&p表示字符指针变量p的地址,即一个二级指针变量的值,而在传参时,这个地址会强制转换成char*的形式,但由于我们对这个位置及其后面的内容时完全未知的,故打印结果为随机值。
6)&p+1表示字符指针变量p的地址往后在移一个地址内存大小的位置,即4个字节。这同样也是一个二级指针变量的值,在传参时,这个地址会强制转换成char*的形式,但由于我们对这个位置及其后面的内容时完全未知的,同时由于这个二级指针变量的值并没有进行存放,因为sizeof()只是编译器进行一个判断,其括号后的内容并不真正执行,故每次生成的&p其值时不固定的。故打印结果为随机值,且与第5条的随机值无关。
7)&p[0]+1放在strlen()中,p[0]表示首元素,&p[0]则表示首元素的地址,strlen()从首元素的地址往后移一个字节大小的地址位置开始,即第二个元素的地址,故打印结果为5。
排除两行错误的代码,实际运行结果如下:
第六组代码:
除去字符指针变量p不能表示整个数组外,其他的分析逻辑与结果和第二组、第四组的时完全一致的,这里也不在赘述了,实际的运行结果如下:
3 二维数组的相关内容
代码如下:
二维数组实际上可以类比为几个(行数)一定大小(列数)的一维数组,因此,其分析逻辑可以从一维数组的相关知识进行引申。对于上面的代码,具体分析如下:
1)a单独放在sizeof()中,此时a表示整个数组,即一个3×4的整型二维数组,因此打印的结果为48。
2)a[0][0]表示数组第一行第一个元素,因此sizef()求的是一个整型量的大小,故打印结果为4。
3)a[0]放在sizeof()中,前面提到二维数组可以看作几个一维数组的结合,a[0]表示二维数组中的第一行,也就相当于一个数组名为a[0],大小为4个整型量的一维数组,即a[0]是一个一维数组的数组名。同时,由于数组名a[0]单独放在sizeof()中,此时a[0]表示整个数组,也就是二维数组的第一行,故打印结果为16。
4)a[0]+1放在sizeof()中,a[0]相当于一个一维数组的数组名,由于数组名a[0]并没有单独放在sizeof()中,此时a[0]表示数组第一个元素的地址,也就是二维数组的第一行第一个元素的地址,a[0]+1则表示二维数组第一行第二个元素的地址,其本质是一个地址,故打印结果为4。
5)*(a[0]+1)放在sizeof()中,前面提到,a[0]+1表示二维数组第一行第二个元素的地址,对其进行解引用操作即为二维数组第二行的第二个元素,故打印结果为4。
6)a+1放在sizeof()中,由于数组名a并没有单独放在sizeof()中,此时a表示数组首元素地址,对于二维数组而言就是第一行的地址,即指向第一行内容的地址。而a+1则表示二维数组第二行的地址,其本质是一个地址,故打印结果为4。
7)*(a+1)放在sizeof()中,前面提到,a+1表示二维数组第二行第的地址,对其进行解引用操作即为二维数组第二行的内容,故打印结果为16。
8)&a[0]+1放在sizeof()中,由于a[0]单独跟在取地址&后面,此时a[0]表示整个数组,即二维数组的第一行,&a[0]+1则表示二维数组第二行的地址,其本质是一个地址,故打印结果为4。
9)*(&a[0]+1)放在sizeof()中,前面提到,&a[0]+1则表示二维数组第二行的地址,对其进行解引用操作即为二维数组第二行的内容,故打印结果为16。
10)*a放在sizeof()中,此时a表述数组首元素地址,即二维数组第一行的地址,对其进行解引用操作即为二维数组第一行的内容。此外,*a <=> *(a+0) <=> a[0],以这样的形式看同样是表示二维数组第一行的内容,故打印结果为16。
11)a[3]放在sizeof()中,表示二维数组第4行的地址,而实际定义的二维数组并不包含这部分内容。但由于sizeof()操作符在进行运算时,并不真正执行其括号内的内容,只考虑括号内内容展现出来的形式即可。因此,在这里尽管没有权限访问到a[3],但它的形式仍然表示是一个4个整型量的数组,故打印结果为16。
12)a[-1]放在sizeof()中,表示二维数组前面一行的地址,与上一条同理,尽管没有权限访问到a[-1],但它的形式仍然表示是一个4个整型量的数组,故打印结果为16。
实际运行结果如下:
4 总结
对于数组名的含义,最重要的还是一开始总结的两点,即:
1. sizaeof()和取地址&后面单独跟数组名,此时数组名代表整个数组;
2. 除以上两种情况外,数组名表示数组首元素的地址;
这也是整个这部分内容的核心。当然,在实际使用中还需要正确的去梳理逻辑,以及掌握数组相关的一些基本知识,尤其是二维数组与一维数组之间的类比与关联。
最后,上面展示的内容实际代码分析为主,内容相对较多,可能存在一些小错误,欢迎各位同行的指正!