A:
Let's say your data structure looks like this:
type
TMyStructure = record
Name: String[40];
Data: array[0..4095] of Integer;
end;
That's too large to be allocated globally, so instead of declaring a global variable,
var
MyData: TMyStructure;
you declare a pointer type,
type
PMyStructure = ^TMyStructure;
and a variable of that type,
var
MyDataPtr: PMyStructure;
Such a pointer consumes only four bytes of the global data segment.
Before you can use the data structure, you have to allocate it on the heap:
New(MyDataPtr);
and now you can access it just like you would global data. The only difference is that you have to use the caret operator to dereference the pointer:
MyDataPtr^.Name := 'Tony Chang';
MyDataPtr^.Data[0] := 12345;
Finally, after you're done using the memory, you deallocate it:
Dispose(MyDataPtr);
Here is some information that shows how to use this dynamic allocation for matrix code writing in a general way:
type
PRow = ^TRow;
TRow = array[1..65520 div SizeOf(TSomeType)] of TSomeType;
{ where TSomeType is the type of the elements of the array }
PMatrix = ^TMatrix;
TMatrix = array[1..16380] of PRow;
procedure InitMatrix(Mat: PMatrix; Rows, Cols: Integer);
var
R: Integer;
begin
GetMem(Mat, Rows * SizeOf(PRow));
for R := 1 to Rows do
GetMem(Mat^[R], Cols * SizeOf(TSomeType));
end;
Once the matrix has been initialized, you reference the individual elements as you would an ordinary two-dimensional array, except that you have to dereference the pointers:
type
TSomeType = Integer;
var
MyMat: PMatrix;
begin
InitMatrix(MyMat, 37, 42);
MyMat^[14]^[10] := 99;
MyMat^[5]^[19] := 12345;
{ etc. }
end;
See Also pointers and assignments
Here is a bit more information on the subject:
Managing Data Segment Size
The "Data Segment too large" error occurs in Delphi 16
bit applications when the size of static data, stack and local heap are
larger than the 64k application limit imposed by Windows. This document
discusses how to identify and measure portions of your code that consume
memory in the data segment, and how to manage this limited resource.
What does the data segment consist of?
--------------------------------------
Task header: 16 bytes of various Windows system information
Static data: Contains global variables and typed constants.
Stack: Stores local variables allocated by procedures
and functions. The default stack size is 16k and can be modified
in the Options|Project|Linker page.
Local heap: Used by Windows for temporary storage and defaults
to 8k. Do not set the local heap to 0. Windows may expand this area of
memory if necessary.
How do I measure total data segment size?
-----------------------------------------
To get the size of a 16 bit Delphi application static data,
stack and local heap for a project, compile the project
and select the Delphi menu item Compile|Information. The dialog
will list the following, given a new project with one form:
Source compile: 12 lines
Code size: 128981 bytes
Data size: 3636 bytes
Stack size: 16384 bytes
Local Heap size: 8192 bytes
A Delphi application starts with the overhead of static data
declared in units that provide the applications initial
functionality. If the only global variable is the form name, the application
will still consume at least 3,636 bytes. Adding a second form increases
the datasize to only 3640 -- an increase of only the size of the global
variable needed to declare a second form.
var
Form2: TForm2; { 4 byte pointer }
The total data segment size, (the sum of static data, stack and local heap) is 28,212 bytes:
Data size: 3,636
Stack size: 16,384
Local Heap size: 8,192
------------------------------
28,212
What portions of my project increase data size?
-----------------------------------------------
-- Variables declared within the interface and implementation sections.
-- Typed constants declared anywhere within the application.
An example of a typed constant declaration:
const
MyConst: integer = 100;
Units declared in the Uses clause and components contain code that may hvae global variables or typed constants. For instance, a TBitBtn adds 180 bytes when added to a project, where at least 132 bytes are accounted for by typed constants and global variables within the Buttons.Pas unit. Adding 10 more TBitBtns to the project does not increase the project size beyond the initial 180 byte increase.
The following sample unit includes comments describing memory usage:
unit Test;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
{ Functions used from the units may have global variables
and typed constants that will increase the size of the
data segment. }
type
{ Class objects are stored on the global heap, not the
data
segment}
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
public
{ MyInteger and MyString are stored
on the global heap. }
MyInteger: Integer;
MyString: String;
end;
const
{ MyConst is a typed constant and is stored in
static data portion of the data segment.
Minimize the number of typed constants. }
MyConst: integer = 23;
var
{ Form1 is a global variable - stored in the static
data portion of the data segment. You should minimize
the number and size of global variables. Form1 is
pointer and uses only four bytes. }
Form1: TForm1;
{ MyGlobalString will consume 256 bytes even if the
string is only a few characters long. }
MyGlobalString: string;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
{ MyLocal is a local variable and is not stored
in the data segment. }
MyLocal: String;
begin
MyLocal := 'Test application';
Label1.Caption := MyLocal
end;
end.
What is the impact of components on the data size?
--------------------------------------------------
Here is a list of components from the Standard, Additional,
Data Access, and Data Control(partial list) pages of the
component pallette. The list shows the compile size after
added a single instance of the component to a new project.
In descending order by data size usage:
Component App Bytes
in Over
bytes 3,636
table 4272 636
batchmove 4272 636
storedproc 4258 622
query 4250 614
database 4036 400
datasource 3886 250
outline 3838 202
bitbtn 3816 180
stringgrid 3794 158
drawgrid 3790 154
maskedit 3762 126
memo 3750 114
report 3722 86
listbox 3704 68
edit 3694 58
tabset 3692 56
combobox 3674 38
scrollbar 3654 18
button 3652 16
checkbox 3652 16
radiobutton 3652 16
radiogroup 3652 16
panel 3650 14
label 3648 12
speedbutton 3646 10
header 3644 8
scrollbox 3644 8
notebook 3638 2
menu 3636 0
groupbox 3636 0
tabbednotebook 3636 0
image 3636 0
shape 3636 0
How do I manage data segment size?
----------------------------------
1) Avoid declaring global variables or typed constants, particularly
large arrays. Instead, declare a type and a pointer to that type.
Then use memory management routines such as Getmem to allocate memory
from the global heap. This reduces the resource hit on
the data segment to the 4 bytes used by the pointer variable. See code
sample below.
2) Be aware of the impact of components. See above. 3) If you have
a large number of strings, or arrays of strings, allocate these dynamically.
Note: strings default to 255 in length -- declare as a specific size
where possible: (i.e. MyShortString: string[20]).
4) A TStringList object can be used to create and manipulate large
numbers of strings.
5) The Pchar type, "pointer to a string", can be used to dynamically
create and manipulate character strings using little data segment space.
See online help under "String-handling routines (Null-terminated)".
6) Information on memory issues is available in Object Pascal
Language Guide, OBJLANG.ZIP and can be downloaded from Compuserve,
DELPHI forum, and the World Wide Web, www.borland.com/TechInfo/delphi/whatsnew/dwnloads.html.
Alternative to global declaration of a large structure
------------------------------------------------------
Here's an example that will waste 32 bytes of data segment, followed by a second example using only 4 bytes, and accomplishes essentially the same task.
1)
{ Declaring TMyStructure causes no change in the data segment
size. }
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ MyStructure declaration causes a 32 byte
increase:
Mystructure pointer = 1 byte
Name = 11 bytes (10 chars + length byte)
Data = 20 bytes (10 * 2 bytes per integer)
}
MyStructure: TMyStructure;
2)
{ Declaring TMyStructure causes no change in the data segment
size. }
PMyStructure = ^TMyStructure;
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ MyDataPtr causes 4 byte increase
for the pointer variable.
}
MyDataPtr: PMyStructure;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
{ Here, resources are taken from the heap. }
New(MyDataPtr);
MyDataPtr.Name := 'Fred';
MyDataPtr.array[0] := 560;
Dispose(MyDataPtr);
end;
You can also put a variable declaration within a class:
type
TMyBigArray = array[1..100] of string
TForm1 = class(TForm)
public
{ This declaration has no impact on data segment
size. }
MyBigArray: TMyBigArray;
end;
var
{ This declaration increases data segment by 25,600 bytes. }
MyOtherBigArray: TMyBigArray;