In the accompanying article, C++ DOS Design tip - Initializing class strings in a separate source file,
we show how you can move the string literals in your DOS applications
to one or two source files. Once you've done this, you
can easily avoid memory errors that result from segment or DGROUP
overflow by moving the class string literals to a separate data
segment. There are three keys to relocating the string literals
this way: declaring the pointers as far variables, using
far pointers for the strings, and compiling the string's
module with the correct segment directives.
Since we're moving the string literals out of the default
data segment in DGROUP, the string pointers themselves need to
be in another segment. Otherwise, the linker won't be able
to resolve the string's address to initialize the pointers,
and you'll see error messages like the following:
Fixup overflow at _TEXT::063, target = puppet::string3 in module PINOCHIO.CPP
By declaring the string pointers as far data variables,
you're forcing the compiler to place the pointers in another
segment. Figure A illustrates how the compiler moves the pointers
when you declare and define them as far variables.
Figure A - If you make a string pointer a far variable, the compiler moves the pointer.
const char* far Puppet::string2 = "Purse 1"; const char* far Puppet::string3 = "Purse 2";
"Purse 1" "Purse 2" |
Default data segment
Puppet::string2 (16-bit) Puppet::string3 (16-bit) |
Far data segment
If you're writing your application using the Large memory
model, all the pointers in the application will be far
pointers by default. However, if you're using the Small
memory model, you'll need to declare all the strings using
the format
static far char* far stringName;
to ensure that the compiler knows the pointer itself is a far pointer (a 32-bit pointer), as shown in Figure B.
Figure B - If you create a far pointer, the pointer will be a 32-bit pointer that can point to data in another segment.
const far char* far Puppet::string2 = "Purse 1"; const far char* far Puppet::string3 = "Purse 2";
"Purse 1" "Purse 2" |
Default data segment
Puppet::string2 (32-bit) Puppet::string3 (32-bit) |
Far data segment
If you don't declare these as far pointers when
you're programming a Small memory model application, the
compiler and linker won't complain. Instead, you'll
get unpredictable results when the program runs, because the compiler
will assume that the pointers in PINOCHIO.CPP are pointing to
memory in the default data segment (because they're 16-bit
near pointers by default) instead of the segment that
contains the string literals. If you use the segment directives
we describe in the next section with out using far pointers,
the code in the file STRMBOLI.CPP will initialize string2
and string3 in a separate segment.
To force the compiler to place Puppet::string2 and Puppet::string3
in a segment other than the default data segment, you'll
need to compile the module that initializes them (in this case
STRMBOLI.CPP) with special command-line settings. For example,
to move the string literals and pointers from STRMBOLI.CPP to
a segment named MY_SEG, click on that source file's name
in the IDE's Project window, choose Local Options from
the Project menu, and enter
-zEMY_SEG -zRMY_SEG -zTFAR_DATA -zS
in the Command Line Options entry field of the Local Options dialog box. (If you're using the command-line compiler, place these options on the command line ahead of the name STRMBOLI.CPP.)
These command-line options set the following parameters:
Now, when the compiler builds the application, it will place the pointers themselves and the text they point to (the actual string literals) in the same FAR_DATA class segment. Figure C shows the final layout of the strings and pointers after applying these segment directives.
Figure C - You can place both the pointers and the string literals in a separate segment.
|
Default data segment
Puppet::string2 (16-bit) Puppet::string3 (16-bit)) "Purse 1") "Purse 2") |
Far data segment
By the way, if you apply these changes to the example in the accompanying
article, you'll need to change the Puppet::showStrings()
function to
void Puppet::showStrings() { cout << string1 << endl; cout << (_near) string2 << endl; cout << (_near) string3 << endl; }
You'll need to make these changes because the output stream
cout will accept only near pointers when you
compile the application using the Small memory model. By casting
the far pointers to string2 and string3
as near pointers (using the _near modifier),
you're telling the compiler to convert the far
pointers to near pointers before sending them to the
output stream.
Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.