Borland Online And The Cobb Group Present:


August, 1994 - Vol. 1 No. 8

Another C++ DOS Design tip - Placing string literals in another data segment

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.

Far variables

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


Far pointers

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.

Segment directives

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.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


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.