`
michael-java
  • 浏览: 18884 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
最近访客 更多访客>>
社区版块
存档分类
最新评论

第三章 数组的解剖学

阅读更多

 

这一章我们来讨论一下数组的内涵,对数组的内部构造进行一次解剖,看看里面究竟隐藏了什么秘密。 有了前面两章对数组名和C语言数组本质的澄清,再来理解这一章的内容,就容易多了。

        在下面的叙述中,笔者会用到一个运算符sizeof,由于在不同的编译器和编译模式下,对一个地址进行sizeof运算的结果有可能是不同的,为了方便讨论,我都假设地址长度为4个字节。

        多数教材在讲述数组的时候,都是把重点放在外部表现形式上,很少涉及数组的内部,只告诉你如何做,却忽视了为什么要这样做。在解释的过程中,还会列出各种各样的表达式,例如:a、a+1、a[0]、a[0][0]、&a[0]、&a[0][0]、*(a+1)等等,让人眼花缭乱。但实际上真正能够用来描述数组内部构造的表达式只有其中的几个。

        上一章讲到,C语言的数组实现并非真正的多维数组,而是数组嵌套,访问某个元素的时候,需要逐层向下解析。仍然以上一章的例子数组int a[7][8][9]来说,第一维元素0的值a[0]是a[0]所代表的那个数组的首地址,这个表达式在C语言的数组里面具有特殊的意义,之所以特殊,不仅仅在于它所代表的东西与一般的地址不同,而且类型也并非一般的地址类型,它的类型叫做数组类型,数组类型这个名称在绝大多数教材中是从来没有出现过的,在C89标准中,也仅仅出现在介绍数组定义的那一段。具有数组类型的地址跟一般类型地址的主要区别,在于长度不一样,对一个一般类型的地址进行sizeof运算,结果是4个字节,而a[0]由于代表了一个数组,sizeof(a[0])的结果是整个数组的长度8x9xsizeof(int),并非4个字节。具有数组类型的地址跟数组名一样都是一个符号地址常量,因此它必定是一个右值。数组类型在数组的定义与引用中具有非常重要的作用,它可以用来识别一个标识符或表达式是否真正的数组, 一个真正数组的数组名,是一个具有数组类型的符号地址常量,它的长度,是整个数组的长度,并非一般地址的长度,如果一个标识符不具备数组类型,那它就不是一个真正的数组。 在后面的章节里,还会再次使用这个概念。

        与a[0]类似的数组类型地址还有a[0][0],a[0][0]是a[0]的下一层数组,因此sizeof(a[0][0])的结果是9xsizeof(int)。类似地,对于一个三维数组:

a[i][j][k]

a、a[x]、a[x][y](其中x、y大于等于0而小于i、j)都是具有数组类型的地址常量,而且都是一个右值。这一点要牢牢记住。正是由这些特殊类型的地址构成了整个数组。

以上结论对于n维数组同样适用。

接下来跟各位一起讨论一下跟数组有关的各种表达式的意义及其类型:

&a[0][0][0]:

&a[0][0][0]仅仅是一个地址,它的意义,仅仅表示元素a[0][0][0]的地址,sizeof(&a[0][0][0])的结果是4。不少人把它说成是数组a的首地址,这是错误的,这是对数组首地址概念的滥用。 真正能代表数组a的数组首地址只有a本身,a与&a[0][0][0]的意义根本就是两回事,真正的数组首地址是具有数组类型的地址,sizeof(a)结果是ixjxkxsizeof(int),而不是4,只不过由于a[0][0][0]位置特殊,恰好是数组a的第一个元素,所以它们的地址值才相同。 而对于a[0]和a[0][0],它们是在数组a内部a[0]和a[0][0]所代表的那个数组的首地址,它们的地址值也是由于位置“特殊”,因此才跟a和&a[0][0][0]一样。这一点一定要区分清楚了。

a+i:

可能有些人会对a+i感到迷惑,数组的首地址加上一个整数是什么呢?它是第一维元素i的地址,sizeof(a+i)为4。

a[i]+j:

跟上面的类似,a[i]+j是a[i]所代表的那个数组的元素j的地址,sizeof(a[i]+j)的结果也为4。

&a:

对数组名取地址在C标准里面是未定义的。这个表达式曾经引起过争论,焦点在于对一个右值取地址的合法性。C89规定&运算符的操作数必须具有具体的内存空间,换言之就是一个左值,但数组名却是一个右值,按照&运算符的要求,这是非法行为。因此,早期的编译器通常规定&a是非法的。但不知道什么原因,现在的编译器都把&a人为地定义成一个比a高一级而地址值跟a一样的地址,但作为比a高一级的地址,有一个行为却非常怪诞,sizeof(&a)的结果跟sizeof(a)相同,这也是人为的痕迹。笔者倾向于把&a定义为非法,应该维护&运算符的权威性,而不是在规定对某个右值取地址为非法的同时,又允许对另一个右值取地址,这是互相矛盾的。

&a[i]和&a[i][j]:

跟&a一样,也是未定义的,同样不符合&运算符的规则。由于a[i]是a[i][j]的上一层数组,有些人可能会想当然地以为:a[i]=&a[i][j],错也,实际上,由于a[i][j]=*(a[i]+j),因此&a[i][j]=&*(a[i]+j),结果是a[i]+j。对于sizeof(&a[i])和sizeof(&a[i][j]),由于是未定义的,因此有些编译器规定其值跟sizeof(a[i])和sizeof(a[i][j])相同,有些编译器却规定为4,就是一个地址的长度。

分享到:
评论

相关推荐

    数组和指针的艺术,就是艺术!

    第三章“数组学的解剖学”深入探讨了数组的内部工作原理,包括如何初始化、访问和操作数组元素,以及数组作为函数参数时的行为。数组作为函数参数时,实际上是传址,即传递数组首地址。 第四章介绍了“[]运算符的...

    [C语言课件ppt]-第三章.zip

    2. 第3章.pptx:这是PowerPoint文件,很可能包含了C语言第三章的完整教学内容。PPTX格式通常用于制作演示文稿,因此这个文件可能会有清晰的幻灯片,每一页可能涵盖一个主题,如变量、运算符、控制结构(如if语句、...

    C语言深度解剖 第一版和第二版(打包)

    《C语言深度解剖》是两本专注于C语言学习与进阶的重要著作,第一版和第二版的结合为读者提供了全面而深入的C语言知识体系。这个压缩包包含的资源,"C语言总结",是对C语言基础知识的精炼概述,涵盖了C语言的关键概念...

    C语言深度解剖(完美版).pdf

    通过《C语言深度解剖》一书,我们不仅能够学到C语言的基础知识和高级技术,更重要的是能够培养起解决问题的能力和严谨的思维方式。正如作者所强调的,真正的精通不仅仅是理论上的理解,更在于实际应用中的熟练掌握。...

    vc实效百例_源代码2

    这个压缩包包含了从第3章到第12章的源代码,涵盖了多个关键知识点,下面将逐一解析这些章节的主要内容。 第03章:基础语法与数据类型 这一章是学习任何编程语言的基础,主要讲解了VC中的基本语法结构,包括变量声明...

    【经典】C语言深度解剖

    - 数组名实际上是一个常量指针,指向数组的第一个元素。 - 可以通过指针运算来访问数组中的元素,例如:`(int*)arr + i`等价于 `arr[i]`。 3. **动态内存分配**: - 使用`malloc()`和`calloc()`函数可以动态地...

    高手写的C语言深度解剖

    - **数组与指针的关系**:理解数组名实际上是指向数组第一个元素的指针这一概念对于有效地使用数组非常重要。 - **字符串处理函数**:熟悉`strlen`、`strcpy`、`strcat`等函数的使用可以帮助处理复杂的字符串操作...

    《C语言深度解剖》PDF

    在本书中,你会了解到C语言的基础知识,包括变量、数据类型、运算符、控制结构(如if语句、switch语句、循环等)、函数的使用和定义、数组和指针的深入理解。C语言的指针是其精髓之一,通过指针,可以实现高效的数据...

    C语言深度解剖.pdf

    ### C语言深度解剖知识点解析 #### 一、引言:C语言的重要性及现状分析 在当前快速发展的信息技术领域中,编程语言作为实现各种软件应用的基础工具,扮演着至关重要的角色。C语言作为一门历史悠久且功能强大的编程...

    再再论指针

    #### 三、数组的解剖学 了解数组内部结构对于深入掌握指针至关重要。数组本质上是一个连续的内存区域,其中每个元素占用相同的字节数。 **内存布局**: - 数组中的每个元素都按照顺序排列在内存中。 - 相邻元素...

    再再论指针 pdf格式 电子书 下载

    1. **数组的解剖学**:了解数组的内部结构和布局对于正确使用数组至关重要。数组是由相同类型的数据元素组成的集合,每个元素都有一个固定的内存地址。 2. **[]运算符的本质**:数组下标运算符`[]`实际上是间接...

Global site tag (gtag.js) - Google Analytics