怎样用动态数组作为过程的参数(或通过过程定义修改动态数组)(100分)

  • 主题发起人 主题发起人 xinjia
  • 开始时间 开始时间
X

xinjia

Unregistered / Unconfirmed
GUEST, unregistred user!
接口部分的过程和数据说明

procedure additemstocombobox(comboboxname:Tcombobox;sjkm:string;xhsz:array of string);

var
hzgxsz,whcdsz:array of string;

实现部分的函数调用
additemstocombobox(combobox3,'cszhen',hzgxsz);
additemstocombobox(combobox4,'dizhi',whcdsz);

函数定义
procedure Tform1.additemstocombobox(comboboxname:Tcombobox;sjkm:string;xhsz:array of string);
var yzm,ysz,i:integer;
begin
with adoquery1 do
begin
close;
sql.clear;
sql.add('select * from '+sjkm);
open;
setlength(xhsz,recordcount);
end;
with comboboxname do
begin
clear;
i:=0;
while not adoquery1.Eof do
begin
items.add(adoquery1.fieldbyname('nr').asstring);
xhsz:=adoquery1.fieldbyname('xh').asstring;
adoquery1.next;
i:=i+1;
end;
end;

功能描述:
打开给定的数据库,将数据库字段分别赋给一个动态数组和一个combobox
编译错误:
过程中setlength(xhsz,recordcount)报告类型不匹配:Incompatible types
 
; 你的程序我想从思路到程序编写应该都没有问题,我在自己的机器试了一下
确实也有这个错误,按Delphi的帮助,SetLength可能用的参数是可以是String变量
也可以是动态数组,我猜可能是String变量与String动态数组的指针可能存在二义
性,当然这只是我的猜想,还请高手指点.

我想对于你来说首先得解决问题,
其实这个地方的String动态数组完全可以用TStringList来代替,
我一直都是这样做的.
procedure Tform1.additemstocombobox(comboboxname:Tcombobox;sjkm:string;xhsz:TStringList);
var
yzm,ysz:integer;
begin
with adoquery1 do
begin
close;
sql.clear;
sql.add('select * from '+sjkm);
open;
//setlength(xhsz,recordcount);取消
end;
with comboboxname do
begin
clear;
xhsz.clear;

while not adoquery1.Eof do
begin
items.add(adoquery1.fieldbyname('nr').asstring);
xhsz.Add(adoquery1.fieldbyname('xh').asstring);
adoquery1.next;
end;
end;
end;
 
为什么不用 var xhsz:array of string ?
 
to 杜宝大侠
用Tstringlist类型,很巧妙,好象是可行的,但你试过吗?我编译能通过但运行中调用
该函数时执行到xhsz.clear(Tstringlist类型清空)时出错,出错信息:
acess violation at address 004c197f in module 'main.exe' read Address 000000'

继续原来的话题:
是不是用动态数组作为参数时传递的只是地址指针。或者是不能用动态数组,只能用
一个指针作为参数,是不是我调用的格式写错了?
 
当然能用动态数组!
只要不是 DLL 调用!(当然如果 Share 了 Memory 也是可以的)。

用 var 传入,用 SetLength() 分配内存,Delphi 修改数组时会
自动更新数组的大小和引用。

----------->《Delphi 5 开发人员指南》:
动态数组是生存期自管理的,利用引用对动态数组进行操作,
在语义上类似于 AnsiString 类型,而不象普通的数组。
 
请ddev详细说说
过程内部用var不是声明了一个内部变量了吗
 
to:xinji
天,你的TStringList创建没有?在xhsz.clear之前,xhsz这个对象是要创建的
过程外部就应该创建好!
 
procedure additemstocombobox(comboboxname:Tcombobox;sjkm:string;xhsz:array of string);

改为:procedure additemstocombobox(comboboxname:Tcombobox;sjkm:string;var xhsz:array of string);

>>过程内部用var不是声明了一个内部变量了吗
var/out 的声明仅表示该变量以引用的方式传递参数 -------- 也就是说:是地址调用。
这跟是否内部/外部变量没有关系。Windows 的 API 大量函数都使用了这种方式,你可以
看看 Windows.pas 的函数声明。
 
to ddev:
真是一句点醒梦中人啊!!!看了你说的,我查了一下书,总算是明白了!多谢多谢!!!
唉! 学了那么多年Pascal这点基本的概念都没弄明白, 真是惭愧啊!!!

to xinji:
我说的TStringList一定可以成功的,类似的东东我做过n个了,至于动态数组的问题
嘛, 有ddev珠玉在前, 我就不班门弄斧了, 惭愧惭愧!

to 斑主大人:
能不能把 我发的 ID=603618 的贴子删了啊 ? 唉,真丢脸啊.让我朋友看到了什么得
了啊!
不过虽然丢脸,但还是学到东西了!
 
呵呵,发上一张贴子时没注意刷新,1:16我发完第一张贴子,1:45后就开始试验动态数组,呵呵,虽说原理
很简单,但是杜宝菜啊,2:25才搞定!惭愧惭愧!
没注意到ddev又发了一张新贴,没想到居然是这种解决方法,真是@$!@$#, 不过高手总能独辟蹊径又岂我
等菜鸟所能臆测的, 关注关注!
 
哇,这里好象都是高手呀,学习学习!
 
昨天一直上不了dfw,可能是中国队胜利后,网民上爆了网。
谢谢杜宝和ddev,这个问题昨天解决了,用动态数组,很爽!因为不太清楚Tstrinlist的
存储机制,加上原来用的都是动态数组,所以没用Tstinglist,我前面用stringlist类型
出错的原因杜宝已说了。只怪自己学艺不精。
用动态数组也需要类型说明,直接用array of string出错!
type
Tarrayofstring = array of string;
TForm1 = class(TForm)
。。。
{过程声明}
procedure additemstocombobox(comboboxname:Tcombobox;sjkm:string;var xhsz,pysz:Tarrayofstring);

(变量说明}
var
hzgxsz:Tarrayofstring;
{过程定义同说明}
procedure Tform1.additemstocombobox(comboboxname:Tcombobox;sjkm:string;var xhsz:Tarrayofstring);
begin
{可以对动态数组定义赋值操作}
end
{过程调用}
additemstocombobox(combobox3,'hzgx',hzgxsz);
各位大侠如果没有指教,我就要送分了
 
帮忙答一下我的问题好不好:
问题:关于控件包使用及子窗体问题!!!!!! ( 积分:100, 回复:1, 阅读:16 )
 
1.到不是动态数组一定要单独说明类型,是因为在Delphi中,函数和过程的开放式数组参数申明与
动态数组申明相同,都是Array of XXXXX,在过程申明中的Array of XXXX被视为开放式数组,在
变量申明中的Array of XXXX被视为动态数组,所以要加上一个新类型来说明.
2.开放式数组本身是为方便在过程中处理不定长度的固定数组而设立的,它本身是固定数组,
所以提示:"Incompatible types"!前天晚上搞了半天最后查Delphi的帮助搞定,看来还得加强
Object Pascal 的学习啊!
3.ddev的方法我不知道你自己试过没有,我想你没有,在答问题前最好自己试试再说!
在delphi中默认的传入参数(不加Const说明的)都是变量型的,找本Pascal的书来看看吧!
 
关于 var Arr: array of string 样式的 DLL 调用,
xinjia 的原码如果加几道处理,肯定是行得通的:

uses ShareMem.
在程序体和 DLL 中引用该单元。那么就可以直接使用
var Arr: array of string 作为传入参数。


to 杜宝:

>>在delphi中默认的传入参数(不加Const说明的)都是变量型的,找本Pascal的书来看看吧!
这句话是错误的,关于函数的参数调用问题,总可以分为两种:

参数复制;地址调用。

procedure MyFunc(V: string);
此时,V 是源传入字串的一个拷贝,你可以自由改变 V 的值 ---------- 但仅仅是在
过程体(MyFunc)中有效,而对于调用函数,根本就没有影响;xinjia 的意图明显是要
返回该数组,所以必须使用 var/out.

procedure MyFunc(var V: string);
此时,V 是源变量的一个地址引用,改变了 V,实际上就是改变了原调用函数的值。

下面是一个关于引用的测试函数:
procedure MyFunc(const V: PChar; var S: string);
begin
S := 'XYZ';
end;

如果在调用函数为:
var
S: string;
begin
S := 'ABC';
MyFunc(PChar(S), S);
end;

虽然 MyFunc 中的 V 被声名为常数,但实际操作时,你会看到 V 被改变了!
这就是引用所造成。

另外一点:
在 Delphi 中,所有的类都是引用。因此处理类似:
procedure MyFunc(MyList: TStrings);之类的函数时,没有必要声名成:
procedure MyFunc(var MyList: TStrings); 这是 Delphi 的一个规范。

>>开放式数组本身是为方便在过程中处理不定长度的固定数组而设立的,它本身是固定数组
我不知道你这句话是从什么地方看到的。对于 D3 以前的动态数组,的确是这样。
也就是说:D3 以前实际上并没有真正的动态数组。声名这种类型的数组时,你必须声名成:
const
MyArrSize = 1024;
type
MyDynamicArray = array[0..MyArrSize - 1] of string;

然后再对数组进行实际的分配操作 --------------- 关于这方面,D3 以前的书籍有
太多的介绍。

D4 之后,Delphi 才真正引入了动态数组的处理。而且可以动态分配多维数组:
SetLength(MyDynamicArray, 12, 100); 就是分配一个 12*100 的二维数组。
 
to:ddev
你我能在这讨论参数的问题,是因xinjia的问题所起,在主人的贴子上我就以xinjia的问题说明。

1、关于 additemstocombobox(comboboxname:Tcombobox;sjkm:string;var xhsz:array of string); 的申明:
我不知道你试过没有,在你第一次发贴子之前,实不相满,我试过,因为没有成功,所以才
在我的第一张贴子中建议xinjia兄用Stringlist试试。
如果你没有,我建议你试试。按你所说的方法,如果成功了,希望你能把你的代码贴出来,
如果分不够,我另外开贴,再加300分给你。

2、关于形参与实参(不知道叫法可对)的问题
对于你上贴所说的基本理论,我想大家都懂,我说的“在delphi中默认的传入参数(不加Const说明的)
都是变量型的”,本意与你所说的“在 Delphi 中,所有的类都是引用。”相同。我承认我表达不清楚,但请
你根据我说话时的情况理解。

3、关于开放式数组与动态数组
关于动态数组,如果你好好看过我和xinjia的贴子就应该知道,我是听说过动态数组的,xinjia不但听说
过还能用。你不用给我们上课,讲什么SetLength分配内存,这点基础我还是有的。
关于开放式数组(Open array parameters)应该译为“开放式数组参数”才对,我看的文章就译作”开放
数组“(《Delphi5开发人员指南》)。而动态数组(Dynamic array)与它是两个概念。相关的解释《Delphi5开
发人员指南》上就有。拜托你再看一下,如果你没有看过,我只能表示遗憾!

4、关于你上贴说的:“uses ShareMem. 在程序体和 DLL 中引用该单元。那么就可以直接使用 ”
我没有试过,也不想再试了。就象我上面说的,我衷心希望你成功,并希望你贴出代码。那么我再加300给你,
绝不食言!

 
to 杜宝:

关于开放数组/动态数组,我确实该说 ----------------- 我搞错了,没有经过测试,主观臆断,
就说了上面这么多的话。你是对的。引一段关于开放数组/动态数组的使用过程的 Delphi 帮助:

---------------------------------------------------------------------------------
Open array parameters allow arrays of different sizes to be passed to the
same procedure or function. To define a routine with an open array parameter,
use the syntax array of type (rather than array[X..Y] of type) in the parameter declaration.
For example,

function Find(A: array of Char): Integer;

declares a function called Find that takes a character array of any size and returns an integer.

Note: The syntax of open array parameters resembles that of dynamic array types,
but they do not mean the same thing. The example above creates a function that takes
any array of Char elements, including (but not limited to) dynamic arrays. To declare
parameters that must be dynamic arrays, you need to specify a type identifier:

type TDynamicCharArray = array of Char;
function Find(A: TDynamicCharArray): Integer;

Dynamic arrays do not have a fixed size or length. Instead, memory for a dynamic array is
reallocated when you assign a value to the array or pass it to the SetLength procedure.

Dynamic arrays are always integer-indexed, always starting from 0.
Dynamic-array variables are implicitly pointers and are managed by the same reference-
counting technique used for long strings. To deallocate a dynamic array, assign nil to a
variable that references the array or pass the variable to Finalize; either of these methods
disposes of the array, provided there are no other references to it. Dynamic arrays of length 0
have the value nil. Do not apply the dereference operator (^) to a dynamic-array variable or
pass it to the New or Dispose procedure.
-------------------------------------------------------------------------------------

正如帮助里所说:

1> 对于参数而言,array of XXXX 形式是开放式数组的表达;而对于变量定义才表示是动态数组。
形式相似,意义却不一样。

2> 如果你要保证传递的是一个动态数组,必须把该动态数组先申明成一个类型,然后传递。

3> 开放式数组在传入时,数组是固定的(大小和长度);而动态数组是不固定的(大小和长度),
它的大小可以通过使用 SetLength() 方法来调整。
也就是说:开放式数组作为参数时,允许重新对每个数组元素进行赋值,却不能用 SetLength()
重新分配数组大小。这是我在前面解答时犯的最大错误。现在予以更正。

4> 开放式数组在传入时有固定的大小,用户可以自己决定开放式数组的起始基数。因此在检查数组
界限时应该用 Low(open_array) / High(open_array) ;而动态数组一般(always 在这儿应该译为什么?
总是?)以 0 为基数(虽然说是 always,但实际上我也没发现可以改变起始基数的可能。在这一点,似
乎应该理解为“总是”,当然用 Low(dynamic_array) / High(dynamic_array) 更可靠)。

5> 动态数组变量实际上是一个显式的指针。动态数组在离开作用域时,会自动释放分配的内存(或者减少
引用计数);如果需要强制(显式)释放动态数组只要设置 DynamicArray := nil 就行了。

6> 虽然动态数组实际是一个指针,但使用数组时却不能用 DynamicArray^ 的形式来访问数组元素或者
用 New/Dispose 的指针分配和释放操作来重新分配或释放一个动态数组。

(这一点倒和类相似,Delphi 中所有类都是引用(或者说都是指针),但却不能用 TStrings^ 的形式来
使用类,或许这里面有什么联系吧,呵呵,猜测而已)

7> 对于字串型动态数组,同样遵循变量引用计数规则(与 AnsiString 一致)。
...

<以上是我的一些理解,不要嫌罗索哦。如果有不妥或者错误,望请指正和补充。占用篇幅,在此表示歉意>


下面是我的测试代码(呵呵,这一次可是真正测试过了):

library DynArr;

uses
ShareMem, //引用 ShareMem 主要是保证传递长字符串时同样有效及
//允许 Delphi 统一管理 APP 及 DLL 中的内存分配/释放
//因为在常规 DLL中,有一句经典的话:
// 不要在 DLL 外部释放 DLL 中分配的内存
//Delphi能够绕开这一点,或许是借助于 VMT 吧(具体也不清楚)
Windows,
SysUtils,
Classes;

{$R *.RES}

type
TStringArray = array of string;

procedure GetDynArrItems(var Arr: TStringArray); stdcall;
const
MAX_ARRAY_SIZE = 100;
var
I: Longint;
begin
SetLength(Arr, MAX_ARRAY_SIZE);
for I := 0 to MAX_ARRAY_SIZE - 1 do
Arr := Format('Dlls Alloc Item %d', );
end;

exports
GetDynArrItems;

begin
end.

------------------------------------------------------------------------------
program Demo;

uses
ShareMem, //引用 ShareMem,与 DLL 一致
Forms,
DemoFrm in 'DemoFrm.pas' {Form1};

{$R *.RES}

begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.

unit DemoFrm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

type
TStringArray = array of string;

procedure GetDynArrItems(var Arr: TStringArray); stdcall; external 'DynArr.dll';

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
Arr: TStringArray;
I: Longint;
begin
GetDynArrItems(Arr);
ListBox1.Items.Clear
for I := Low(Arr) to High(Arr) do
ListBox1.Items.Add(Arr);
end;

end.
 
高手过招,有点眼花撩乱,
to 杜宝,ddev:你们的提示都是可行的,我都调试通过了,多谢指点,有些小地方如声明方面
的争论或是忽略,有点编程经验都可以根据错误提示稍加变通调试通过,至于变通的原理,
由于本人编程也是急就章式的半路出家,也不求甚解。
我使用例程的目的
对数据表记录的有些字段输入,希望以代码形式保存,如职业一栏,职业名称放在一个
combobox里,输入时进行选择,存储时只存储相应的代码,代码用数组保存。代码数组下标
正好是combobox.itemindex.这个数组是动态数组。
可以这定义的字段有:文化程度/民族/职业/省市/职务等
form的onactive事件中,使用例程,增加各combobox条目,定义各代码数组

本人在最近买的一本书《delphi手册》得到一些启示,这本书除了目录稍嫌简略外,简直
句句骨头,摘录一段:
。。。

可以对一个函数或过程以一个开放数组来声明一个参数,如果例程不需要修改数组则确保以
const声明这个参数;或者以var声明,如果例程修改了数组内容的话。
对一个开放数组的声明看起来就像对一个动态数组的声明,。。当作为一个参数使用时
一个没有索引类型的数组声明是一个开放数组,当用来声明的一个局部或全局变量、一个
类里面的一个域,或者一个新类型时,一个没有索引类型的声明则意味着一个动态数且。
可以传递一个动态数组给一个以开放数组声明参数的例程,并且例程可以存取动态数组
的元素,但不能改变数组的大小,因为开放数组和动态数组的声明相同,把一个参数声明
为一个动态数组的唯一办法是,为动态数组类型声明为一个新的类型标识符。
。。。
 
我的贴子与ddev几乎同时写的,大致意义相同,ddev更详细。

透了,透了!起码是目前感觉上。动态数组的参数调用方面。
 
后退
顶部