关于数组的一点笔记
pascal吧
全部回复
仅看楼主
level 1
黄金止手 楼主
静态数组
下标可以是顺序类型(整数、字符、枚举、Boolean)或其子域,但不能是ByteBool系列。
下标的序数(对于整数是其值,对于其他类型是ord()的值)必须在 SizeInt 的范围内。数组整体大小也必须在 SizeInt 范围内。
即 32 位程序的数组下标必须在序数值 -2147483648..2147483647 内,数组整体大小必须在 0..2147483647 内。
array [0..MaxLongInt] of Char 或 array [2..$80000000] of Char 无法通过编译,而 array [0..MaxLongInt-1] of Char 可以。
但实际上 Windows 10 下 32 位 FP 程序基本无法分配大于 1.9GB 的空间,所以平台限制比语法限制更苛刻。
静态数组可以直接用赋值来复制,赋值后仍是两个数组。
动态数组
长度必须是非负数,且在 SizeInt 的范围内。在 setlength 里令长度为 LongInt 范围内的负数不会产生编译错误,但会产生运行错误。
同样的,实际上在具体系统中很可能不能分配到这个长度。
动态数组下标必须是整数,且从 0 开始到长度减一。
简单地使用 DynArrA:=DynArrB 会令 DynArrA 和 DynArrB 成为同一数组的两个引用。故而需要用 Copy 来复制数组。
动态数组形式上可以是多维的,但多维动态数组内存空间并不一定连续分配,不像多维静态数组一样是个整体。多维动态数组更像是一维动态数组的迭代。
无论是静态数组还是动态数组,传入方法(过程/函数)时参数都不能写成匿名类型(即 array [TIndex] of Atype / array of Atype 形式),必须给数组类型一个类型标识符,方可将其照原样传入方法。
开放数组
开放数组不是真正的变量类型,只是一种只出现于参数列表中的“伪”类型。
声明形式是参数列表中写 array of AType,可以加 const/var/out 修饰符。
开放数组只能是一维的,而且在方法内部是静态的,不可改变长度。但在调用方法时,可以传入不同长度的静态或动态数组,还可以用 A[left..right] 的形式传入数组的一部分。
在方法内部,开放数组的下标是从 0 开始到长度减一的整数。可以通过 high(A) 获得开放数组下标的最大值。
调用方法时,一个开放数组参数实际上是两个参数:数组首地址和数组最大下标(即长度减一)。和直接这么写的区别是开放数组参数可以做范围检查,安全性更好。
开放数组参数若要传递,只能传给另一个开放数组参数。
参数列表中的修饰符对数组的影响
Pascal 有 var/const/out/constref 四种参数修饰和无修饰参数。
constref 在 FP 的 2.6.1 版本后才得到支持。
out 仅在 FP 的 ObjFPC 和 Delphi 模式下得到支持。
无修饰表示传值,等于有一个隐含的 形参:=实参 赋值语句。
对于静态数组和开放数组,无修饰参数会导致在栈上把数组复制一遍。对于动态数组则是进入方法时引用数加一,离开时引用数减一。
var, out, constref 修饰表示传递引用,var 表示该参数可变,out表示该参数可变且传入值无意义,constref 表示该参数被视作不可变的常量。
out 和 var 的实际区别在修饰动态数组和动态字符串之时。var 和 constref 修饰的动态类型引用数不会自动改变,但 out 所修饰者在进入方法时引用数减一或被清空。
const 表示传递的参数被视作常量,但究竟是传值还是传引用是编译器决定的。对于开放数组和动态数组,const 等价于 constref 。
部分很短的静态数组用 const 修饰可能会发生按值传递(即发生复制)。
考虑到目前 FP 在传值参数中都把数组复制到栈上,对于静态数组和开放数组参数,最好加 var/const 修饰。数组参数能加 const 就加 const,需要修改时加 var ,实在不行时(如外部不能改变,但内部又要修改时)才写无修饰参数。
2016年08月24日 10点08分 1
level 1
黄金止手 楼主
关于无类型参数
Pascal 中任何参数都可以按引用传给无类型参数。如 FillChar 的第一个参数就是无类型参数。无类型参数只能按引用(地址)传递,必须要用 var/out/const/constref 修饰,这里 const 和 constref 等价。
声明格式为 var x 或 const x。
无类型参数只能传给另一个无类型参数,const 所修饰者不可传给 var/out 。
对于 var/out 的无类型参数,可以用强制类型转换将其转为任何类型的左值。const 所修饰者,只能转换成右值。
无类型参数可以用 @ 运算符取地址,实际上这个地址就是栈上压入的数值。
无参数类型在 C/C++ 中无对应,但起到了void* / const void* 的功能。
假如 C/C++ 函数用了无类型指针,它们在 Pascal 程序中声明时也可以用无类型参数来写,这样看起来更加有 Pascal 的风格。
2016年08月24日 11点08分 2
level 1
黄金止手 楼主
文字常量字符串与字符数组
字符串文字常量(包括 const s=... 定义的普通常量)可以赋值给 PChar (字符指针)变量,也可以传递给 array of Char 开放数组参数和无类型 const 参数。同时也可以传给 string 变量。
在赋值给 PChar 或传递给 array of Char 和无类型 const 参数时,内存中字符串文字常量会被处理成字符数组,末尾会自动添加 #0 终止符。
字符串文字常量若被传给 string 类型的参数或被赋值给 string 变量时,则其在内存中会被处理成有前导长度字节的通常短字符串,末位不一定会添加 #0 。
若出现了类似 'Hello World'[1] 的字符串文字常量带下标的表达式,则该常量也会被当成短字符串处理。
假如同一个字符串文字常量(包括普通常量)同时有这两类行为,那么它会在内存中存在两份:一份格式是字符数组,首字符下标为 0 ;另一份是格式是短字符串,首字符下标为 1 。
对字符串文字常量(包括传递给开放数组后的)求 sizeof() 的结果是字符串长度。不计添加的 #0 。
若把字符串文字常量传递给开放数组,那么方法内的数组长度也是字符串长度,不计添加的 #0 。
假如声明了
procedure ShowCharAray(const s:array of Char);

ShowCharArray(['H', 'e', 'l', 'l', 'o'])

ShowCharArray('Hello')
在执行时,得到的数组是完全一样的。
区别是前者的字符数组在栈上,生存期为过程运行期;后者的字符数组在全局区上,生存期为整个程序。且后者的数组后面一定跟随 #0 ,可以转成 PChar 类型而不出错,但前者不一定。
2016年08月24日 18点08分 3
level 1
黄金止手 楼主
补充
之前对无类型参数描述有误
无类型的 constref 只能传入能与无类型 var 相匹配的参数。占用内存空间的常量无法传给无类型 constref 。
开放数组参数可以传入当场构造的临时匿名数组,临时数组会存在于栈。前提是不能用 var 或 out 修饰。
即若声明了
procedure ShowIntArray(const x:array of LongInt);

ShowIntArray([1,2,3,4])
是合法的。
若开放数组为无修饰参数,则即使传入匿名数组,在传入参数时仍然会发生复制。即栈上会存在 2 份临时数组。而用 const 或 constref 修饰时就不会发生复制。
所以要再重复一遍,传入静态或开放数组参数时,最好用 const / var 修饰,尽量不要用无修饰参数。
开放数组可以传入数组的一部分。传入部分数组时参数可以是无修饰或任意修饰。
假设定义了
var
IntArrayA:array [1..1024] of LongInt;
则使用
ShowIntArray(IntArrayA[10..19])
可以传入 IntArrayA[10] 到 IntArray[19] 这部分,在过程内部 x[0] 等于 IntArray[10] ,x[9] 等于 IntArray[19] 。
2016年08月24日 18点08分 4
level 5
mark
2016年08月25日 01点08分 5
level 5
S
2016年08月25日 03点08分 6
1