C和C++没有真正的数组
2010-07-20 13:56
246 查看
在其它高级语言里,不管是定义(声明)还是引用,
a[i]
或
a[3]
都是一个整体。在
C/C++
里,却是一个表达式:
a[i]
是运算符
[]
连接两个实体
a
和
i
。
说
C/C++
并没有数组
,
有以下几条理由。
理由一:
C
里没有数组形式。
“数组”名
a
本身就是一个指针,与常规指针不同的是,它是一个不能移动的所谓常指针。
如在函数外有定义:
float a[3] = {1.0, 2.0, 3.0};
首先在初始化数据段分配一块能容纳三个
float
数的空间,并填入三个初始值,然后定义一个名为
a
指向
float
数据流的常指针,并使其指向该区域的首字节。
理由二:“数组”的定义,其实最终是对指针的定义。
说“指向
float
数据流”,和说“指向
float
型数组”,是两个概念。共性是,计算偏移量(我不说移动,因为常指针是不能移动的。)时,计算单位都是
float
型数据的字节数。但是,数组是有边界的,你的下标不能超出边界。而偏移量可以超出数据流的边界(后果自负)。
很多书里说,
C
“数组”没有边界检查,是为了运行效率。但是,对边界的检查,系统开销并不大。
C
里的“数组”其实是个数据流,它的边界只有一头:常指针所指向的下边界。
理由三:数组名和下标竟然可以互换。
我们要访问上面那个数据流的第
2
个数据,可以使用
a[1]
,也可以使用
*(a + 1)
。两者完全等价。我怀疑,
C
的作者所提供的
a[i]
,仅仅是
*(a + i)
的同义词。按照加法交换率,显然,
*(a + i)
等于
*(i + a)
。那
i[a]
是不是也等于
a[i]
呢?测试结果:等于。更奇怪的是,不但
i[a]
等于
a[i]
,
1[a]
也等于
a[1]
!
看看下面的相等关系:
a[1]
等于
*(a + 1)
等于
*(1 + a)
等于
1[a]
上面的怀疑或许有点道理了。
理由四:
a[i]
无非是
*(a + i)
的同义词。
对“数组”的访问,最终总是通过指针的。其基本形式是:
*(a + i)
。
“数组”名是一个常指针,总是指向该区域的首址。“下标”其实是一个逻辑偏移量。说它是“逻辑”的,意思是在计算时,需要乘以步长(数据的长度)。但是,这个“乘法”对你是透明的,不必关心它。指向所访问数据的是常指针“加”偏移量。
在
X86
系列
CPU
的指令系统里,有一个基址变址寻址方式。这种寻址方式和
C
对“数组”的访问方式很相似。常指针相当于基址,偏移量相当于变址。
我怀疑,这个基址变址寻址方式是为
C
访问“数组”而增加的。
理由五:
C
“数组”没有上边界。
对下面的定义
float a[3] = {1.0, 2.0, 3.0};
我们不仅可以访问
a[0]
、
a[1]
和
a[2]
,还可以访问
a[3]
、
a[4]
等。
C
数组不知道自己的一亩三分到哪里为止。用
C/C++
开发,与用其它语言不同,编程员必须记住自己定义的数组有多大。自己的家没有栅栏,跑到邻居割韭菜没人管,但后果自负。
理由六:对“多维数组”的访问总是可化解成对“一维数组”的方式。
诚然,不管是用哪种语言编程,最终生成的目的码中,数组总是被转换成一块连续的存储区(即它是线状的)。不同的是,在
C
源码里,所有的数组,不管是几维的,都是线状的。在源码层面,都可以看作是一维的。定义了
int a[3][4];
可以用
a[2][3]
,也可以用
a[11]
访问其第
2
行、第
3
列元素。
结论
C
里使用下标运算符
[]
,无非是使指向一串等类型元素的指针对该区域的操作看起来像操作数组而已。没有这东西,习惯了其它高级语言数组操作的编码员会觉得不习惯。
a[i]
或
a[3]
都是一个整体。在
C/C++
里,却是一个表达式:
a[i]
是运算符
[]
连接两个实体
a
和
i
。
说
C/C++
并没有数组
,
有以下几条理由。
理由一:
C
里没有数组形式。
“数组”名
a
本身就是一个指针,与常规指针不同的是,它是一个不能移动的所谓常指针。
如在函数外有定义:
float a[3] = {1.0, 2.0, 3.0};
首先在初始化数据段分配一块能容纳三个
float
数的空间,并填入三个初始值,然后定义一个名为
a
指向
float
数据流的常指针,并使其指向该区域的首字节。
理由二:“数组”的定义,其实最终是对指针的定义。
说“指向
float
数据流”,和说“指向
float
型数组”,是两个概念。共性是,计算偏移量(我不说移动,因为常指针是不能移动的。)时,计算单位都是
float
型数据的字节数。但是,数组是有边界的,你的下标不能超出边界。而偏移量可以超出数据流的边界(后果自负)。
很多书里说,
C
“数组”没有边界检查,是为了运行效率。但是,对边界的检查,系统开销并不大。
C
里的“数组”其实是个数据流,它的边界只有一头:常指针所指向的下边界。
理由三:数组名和下标竟然可以互换。
我们要访问上面那个数据流的第
2
个数据,可以使用
a[1]
,也可以使用
*(a + 1)
。两者完全等价。我怀疑,
C
的作者所提供的
a[i]
,仅仅是
*(a + i)
的同义词。按照加法交换率,显然,
*(a + i)
等于
*(i + a)
。那
i[a]
是不是也等于
a[i]
呢?测试结果:等于。更奇怪的是,不但
i[a]
等于
a[i]
,
1[a]
也等于
a[1]
!
看看下面的相等关系:
a[1]
等于
*(a + 1)
等于
*(1 + a)
等于
1[a]
上面的怀疑或许有点道理了。
理由四:
a[i]
无非是
*(a + i)
的同义词。
对“数组”的访问,最终总是通过指针的。其基本形式是:
*(a + i)
。
“数组”名是一个常指针,总是指向该区域的首址。“下标”其实是一个逻辑偏移量。说它是“逻辑”的,意思是在计算时,需要乘以步长(数据的长度)。但是,这个“乘法”对你是透明的,不必关心它。指向所访问数据的是常指针“加”偏移量。
在
X86
系列
CPU
的指令系统里,有一个基址变址寻址方式。这种寻址方式和
C
对“数组”的访问方式很相似。常指针相当于基址,偏移量相当于变址。
我怀疑,这个基址变址寻址方式是为
C
访问“数组”而增加的。
理由五:
C
“数组”没有上边界。
对下面的定义
float a[3] = {1.0, 2.0, 3.0};
我们不仅可以访问
a[0]
、
a[1]
和
a[2]
,还可以访问
a[3]
、
a[4]
等。
C
数组不知道自己的一亩三分到哪里为止。用
C/C++
开发,与用其它语言不同,编程员必须记住自己定义的数组有多大。自己的家没有栅栏,跑到邻居割韭菜没人管,但后果自负。
理由六:对“多维数组”的访问总是可化解成对“一维数组”的方式。
诚然,不管是用哪种语言编程,最终生成的目的码中,数组总是被转换成一块连续的存储区(即它是线状的)。不同的是,在
C
源码里,所有的数组,不管是几维的,都是线状的。在源码层面,都可以看作是一维的。定义了
int a[3][4];
可以用
a[2][3]
,也可以用
a[11]
访问其第
2
行、第
3
列元素。
结论
C
里使用下标运算符
[]
,无非是使指向一串等类型元素的指针对该区域的操作看起来像操作数组而已。没有这东西,习惯了其它高级语言数组操作的编码员会觉得不习惯。
相关文章推荐
- c++ 没有默认构造函数类不能用作动态分配数组的元素
- java运行长度10000的数组堆栈溢出,c++和Delphi都没有
- C++ 杨辉三角,没有使用数组
- C/C++没有数组
- C++库研究笔记—— [真正正确?]多维数组动态分配正确方式?(之一)
- C++中数组与MATLAB mxArray相互赋值
- C++动态数组分配与释放
- C++复习 04 数组和指针
- c++ hash_set 删除原数组重复数据,且不改变原数组顺序
- 一个数组存储很多英文字母,输出没有存储的字母
- 重学C++ 函数指针数组
- 怎样向一个c++函数传数组、Linux下怎么显示unicode字符、Linux的地址空间
- 【CPP】C++ 里数组 new 和 delete 问题
- c++ 模板数组的引用
- Java中JNI的使用详解第五篇:C/C++中操作Java中的数组
- C++中函数指针数组的使用
- [学习笔记]C和C++中数组的一些小心得(一)
- java和c、c++中数组的区别
- C++ 数组参数
- C/C++数组名与指针区别深入探索