www.digitalmars.com [Home] [Search] [Contents]

Compiling Code

The Digital Mars compilers run under Win32 as both a GUI application (the IDDE) and as a console application. Code can be compiled and executables built for 32 bit Windows, 32 bit Windows console, 16 bit Windows, 16 bit DOS, and 32 bit DOS (with the addition of the X32 dos extender).

This chapter describes how the compiler works by default and how to customize the compiler's output. It covers both 16-bit and 32-bit compilations.

For more information about:

What's in This Chapter

A Simple Example

To illustrate compiling the classic hello world! program using the console compiler, create a text file hello.c with the contents:
	#include <stdio.h>

	void main()
	{
	    printf("hello world!\n");
	}
Compile and link it with the following command:
	sc hello
which will compile hello.c to create the object file hello.obj which is linked to generate the executable hello.exe. To run the executable type:
	hello
which will print:
	hello world!

How Digital Mars C and C++ Compiles Code

Compiler options customizes the way Digital Mars C and C++ compile programs. Specify compiler options via the Build page in the IDDE's Project Settings dialog box or by passing options as command line arguments to the SC control program. All compiler options are optional; the default settings are oriented towards generating a complete compiled program.

The following sections explain how the compiler handles various aspects of compilation and how to control it.

Overview of the compilation process

Digital Mars C++ is an extremely fast one-pass compilation system with highly effective register utilization. An optional global optimization facility provides tight and efficient object code. Run the compiler from within the IDDE or more traditionally using SC from the console command line.

The compiler executables can compile either C or C++ files. The preprocessor is integrated in; but for specialized applications a separate standalone preprocessor is also included.

As it runs, the compiler reads the source files, executes all preprocessor functions, checks for any syntax errors, optimizes the program, generates machine code, and outputs an object (.obj) file suitable for input to the linker.

Startup code

Programs compiled with Digital Mars C++ call one of several startup functions before passing control to a program's main() routine. (The startup function called depends on what kind of application is being built, as described in __acrtused.) This startup function performs any required initializations such as initializing data and running the static constructors.

There are several files that contain startup functions (all with different names) in several subdirectories of the Digital Mars C++ \sc\src directory.

Specifying compiler output

By default, Digital Mars C++ compiles and links the source files given on the command line and generates an executable file whose name is the same as the name of the first source file with the extension replaced with .exe. To generate different files or additional files use the SC options described below or via corresponding options in the Compiler Output subpage on the Build page of the IDDE's Project Settings dialog box.

Support for C language features

Support for C++ language features

Digital Mars C++ supports run time type identification, exception handling, and overloading of new[] and delete[]. These new C++ language features are not enabled by default; to enable them, you need to specify compiler options as described below.

Suppressing warnings and errors

By default, the Digital Mars C and C++ compilers generate warnings about certain constructs that, while legal, are likely mistakes. Warnings can be disabled individually or as a group. Warnings can also be treated as errors. In the IDDE, these are set via the Compiler Output subpage on the Build page of the IDDE's Project Settings dialog box. See "Warnings and associated warning numbers" for messages.

Dynamic profiling

See Trace Dynamic Profiling.

Using precompiled headers

If your program uses a large header file or numerous small headers, the compiler spends considerable time compiling the same code over and over again. To save compile time, you can precompile a header file; the compiler can load a precompiled header faster than it can a text header file. Digital Mars C++ includes a precompiled header only once in a file, regardless of how many #include statements for it appear.

Information about compiler options for using precompiled headers follows this section.

How the compiler precompiles a header

To precompile a header, the compiler writes out the header file's symbol table. It saves only the global symbol table, including any macros the header defines. A header you plan to precompile should, therefore, contain only declarations and no definitions.

For example, the following declarations are appropriate for use in precompiled headers:

	extern int abc;
	int foo(int x); 
	inline void g(x) { foo(x); } // inline function definition 
	#define MACRO(x) asdf(x) 
These will not work:
	int def = 3; // a definition
	void g(x) { foo(x); } // function definition 
To use a precompiled header in your program, follow these steps: You should create a directory specifically for your program that includes its source files and makefile. When compiling, this directory is the current directory, and the compiler automatically writes the precompiled headers there. A program should have its own directory because precompiled headers are highly specialized, and it is unlikely that a precompiled header for one program can work with another.

#include the precompiled header in the source as you normally would. For example, to use a precompiled version of stdio.h in a program, write:

	#include <stdio.h> 
	main()
	{ 
	    printf("hello world\n");
	} 
Note: #include directives for precompiled header file(s) should be the first line(s) in a source file other than comments. If other code precedes the #include directive for a precompiled header, you need to ensure that your code does not affect compilation of the header file.

The program's makefile must precompile the header file and include the precompiled header in the list of dependencies for all the source files that #include it. For example, this is the makefile for the hello world program:

	stdio.sym : stdio.h makefile
		sc -HF stdio.h 

	hello.exe : hello.c stdio.sym
		sc -H hello 
The first two lines are for the precompiled header. The name of the precompiled header is the same as the text header, except it ends in .sym instead of .h. The SC -HF command precompiles the header file, creates a .sym file and places it in the current directory. The precompiled header depends not only on the text header file, but also on the program's makefile. This is because changes in the makefile, such as using a different memory model or target CPU, change how Digital Mars C++ precompiles the header.

The last two lines above are for the program's source file. The executable file is dependent not only on its source file, but also on the precompiled header. The -H option tells the compiler that when it looks for a header file, it should first look in the current directory for .sym files.

Using a project precompiled header

If many of your source files #include the same files, you might want to create a project precompiled header that #includes several text header files.

For example, if the files in your project all #include stdio.h, string.h and myglobals.h, you could create a project header file project.h that contains just these lines:

	#include <stdio.h>
	#include <string.h> 
	#include "myglobals.h" 
Precompile project.h with this command:
	sc -HF project. h 
Then compile them with the -H and -HI options:
	sc file1 file2 file3 -HIproject.h -H 

Using a RAM disk with a precompiled header

To make a precompiled header file even faster, place it on a RAM disk. To let the compiler know where the precompiled file is, make some changes in the makefile. First, precompile the header file with the -HF option to specify where to place it. Then, compile the source files with the -HD option to specify where to look for it.

For example, if your RAM disk is drive e, the makefile for the hello world program looks like this:

	TMP= e: 
	$(TMP)stdio.sym : stdio.h makefile
		sc -HF$(TMP) stdio stdio.h 

	hello.exe : hello.c $(TMP)stdio.sym
		sc -HD$(TMP) hello 
or, with automatic precompiled headers:
	TMP=e: 

	hello.exe : hello.c
		sc -HD$(TMP) -HX hello 

Avoiding problems with precompiled headers

To make the best use of precompiled headers, follow these rules:

Precompiled header options

Corresponding IDDE options are in the Header Files subpage on the Build page of the IDDE's Project Settings dialog box. Note: If the header file has changed since the last time you precompiled it, the compiler uses the older precompiled header file. Updating the precompiled header file should be done by the makefile. Using -HX improves compile times for most programs. Instances where this might not be the case include:

Including header files

You can tell the compiler to include header files and where to look for them by using the options below.

Choosing a memory model

Memory model choices trade off between meeting minimum system requirements, maximizing code efficiency, and accessing as much available memory as possible.

For more information, see Chapter 7, "Choosing a Memory Model." To specify the memory model, use the SC options described below or use the Memory Models subpage on the Build page of the IDDE's Project Settings dialog box.

For more information on memory allocation, see Chapter 5, "Using Assembly Language Functions."

Specifies the memory model to compile for. The default for SC is the Win32 model (-mn).
		Code	Data	Model
		size	size
	t	64Kb total	Tiny (.COM program)
	s	64Kb	64Kb	Small (default) 64KB 64KB 
	m	1MB	64KB	Medium  
	c	64KB	1MB	Compact  
	l	1MB	1MB	Large 
	x	4GB	4GB	DOSX 32-Bit Extended:
				requires DOSX DOS Extender
	p	4GB	4GB	Phar Lap 32-Bit Extended:
				requires Phar Lap 386|DOS Extender SDK 
				(5.0 or newer). 
	n	4GB	4GB	Win32 (Win32s, Windows 95, 98, NT, 2000)
	f	4GB	4GB	OS/2 2.0 (Flat)
Note: For Windows NT, it is imperative that SS==DS==ES at all times. Otherwise the NT API functions will crash. (CS need not be equal to SS, FS is reserved for thread-related information, and GS is available for general use.) Thus, SS==DS==ES for the NT (-mn) memory model.

Here are some example combinations of memory model specifiers:

	-ms		Small model with SS == DS 
	-msw		Small model SS != DS 
	-mc		Compact model 
	-mt		.com file 
	-mluw		Large model, SS != DS, Reload DS 

The program stack

In all memory models, the stack grows downward toward statically allocated data. If the stack grows larger than its allocated size, statically allocated data are destroyed, and the program performs unpredictably. To check if the stack grows past the allocated limit, use the special function called _chkstack.

Call _chkstack from functions that potentially use large amounts of stack space, such as recursive functions. If the stack exceeds the allocated size, _chkstack aborts the program with the message:

	Stack overflow 
Use the -s compiler option (see below) to generate stack-overflow checking code on entry to every function, although this increases program size and reduces speed. Use stack-overflow checking if you suspect stack-overflow errors (a symptom is a mysterious crash).

Controlling stack size for DOS programs

You can control a DOS program's stack size in the following ways: For Win16 and Win32 compilations, use the linker's /STACK switch to set the stack size.

For a DOS program, do not use the linker /STACK parameter to set the stack size; use one of the methods described above. The default stack size is 8192 bytes for 16-bit compilations and 16KB for 32-bit compilations.

Aligning data objects

In 16-bit compilations, the compiler align data objects on 16-bit word boundaries. This improves the execution speed of code running on CPUs with a 16-bit data bus (like the 80286). The default alignment for 32-bit compilations is on 32-bit boundaries. The default alignment of structure members depends on the memory model used for the compilation. You can change the alignment within structures via the -a compiler option (see the section "-a Specify structure alignment" later in this chapter), so that structure members are aligned on different boundaries. This option is useful for defining a struct that maps onto a hardware device or a predefined data element.

You can control alignment only within structures; the compiler still aligns all other data objects on word or dword boundaries.

Struct member alignment can also be specified with the #pragma pack preprocessor directive. Refer to Chapter 3, "Digital Mars C++ Language Implementation" for more information.

The C++ compiler does not generate structs with a size of 0 bytes if there are no nonstatic data members; the minimum size of a struct is 1 byte. This prevents new() from returning 0 when it tries to allocate an instance of a struct.

Warning: Each file referencing a structure must be compiled with the same type of alignment. If two files compiled with different alignment reference the same structure, any erroneous results could be hard to debug.

You can set this option from the IDDE in the Compiler subpage on the Build page of the IDDE's Project Settings dialog box.

Compiling for debugging

Programs compiled with Digital Mars C and C++ can be debugged using many third-party debuggers, as well as the Digital Mars C and C++ debuggers.

There are two basic types of debugger that can be used with Digital Mars C and C++ programs: fully symbolic debuggers (those that work with symbolic information, like the Digital Mars debuggers or Microsoft Corporation's CodeView), and partially symbolic debuggers (those that require only source code line numbers and map file information, such as Microsoft's Symdeb).

Compatibility with third-party C debuggers

Fully symbolic debuggers that are compatible with the data format used by Microsoft CodeView should give acceptable results when used to debug C programs compiled with Digital Mars C++, provided they can handle both C and Pascal function calling conventions. Some information on local variables may not be available due to the automatic register assignments carried out by Digital Mars compilers. (Digital Mars C and C++ make intelligent use of registers within functions, automatically assigning frequently used variables to registers. This register assignment uses any available register and not simply SI and DI.) Some debuggers cannot handle variables in registers other than SI and DI and consequently cannot display or track them.

The Digital Mars C and C++ debuggers can display, modify, and track all the local variables within a function.

Compatibility with third-party C++ debuggers

Although some other third-party C debuggers can partially debug C++ programs, they cannot obtain or handle all the information needed to fully debug C++ code. The main difficulties are these: To fully overcome these problems, you must use a C++ debugger. The Digital Mars C++ debugger provides full debugging facilities for C++ programmers. It lets class definitions be fully expanded, C++ scope rules be understood, pointer conversions be done automatically, overloaded operators be presented correctly, and function prototypes be examined.

Overview of compiler debugging options

If the compiler always generates full debugging information for all variables, then the object files would be very large, and the linker would have to remove a great deal of redundant information. You can control the level of debugging information the compiler provides with the -g option (see the section "-g Generate debugging information" later in this chapter). The compiler can produce five levels of debugging information: Compiling with any level of debugging information significantly increases the size of the object file. The size of the executable file also increases, often doubling.

Debugging options

Linking for debugging

When compiling for debugging, the compiler automatically passes the correct information to the linker. If you use a separate link step, you need to pass the appropriate options to the linker yourself.

These are the options for Digital Mars OPTLINK or Microsoft LINK for all levels of debugging information:

Note: When creating debugging information, OPTLINK uses CodeView Version 4 format if any object module in the link step contains CV4 records. In this case, it throws away any CV3 records it encounters. OPTLINK creates an executable with CV3 debugging format only if every debug record is in CV3 format. To retain CV3 records in objects, run OPTLINK with the /CO:3 or /NOCVPACK options, and then run Microsoft's CVPACK.

See the User's Guide and Reference for more information on using the Digital Mars debuggers.

Notes on debugging with CodeView

Unlike the Digital Mars C++ debugger, which is statement oriented, the Microsoft CodeView debugger is a line oriented source debugger. At the source code level, the smallest unit of code CodeView can handle is a line, not a statement. To use CodeView most effectively with Digital Mars C++, follow these rules: Refer to Microsoft's documentation for information on CodeView.

Debugging aids in the run-time library

There are a number of features in the run-time library that help detect common run-time errors: If you optimize the code you are debugging, remember that optimization moves variables and sections of code and creates only one scope per function. Avoid using the same variable names in nested scopes.

Specifying linker and assembler options

The compiler options described below let you run third party linkers and assemblers on the SC command line, or pass options to the linker or assembler.

Controlling segmentation

When writing large 16 bit Windows programs, you sometimes need to place functions that frequently call each other into the same segment. This optimization helps speed execution. This section explains how to segment your code. Use these methods independently or in any combination.

Preventing data segment overflow

Many large programs run out of space in the default data segment DGROUP. The solution is to move some data out of DGROUP into other segments. Here are some methods you can use: For information on declaring data as far and placing data in the code segment, see Chapter 7, "Choosing a Memory Model."

Data declared as far should be data that is relatively large and is accessed relatively infrequently, as more overhead is required to access those data.

If another module should reference far static data, declare it as far, as in:

	extern int __far array[]; 
Otherwise, the compiler assumes that the data is in the default data segment DGROUP. Digital Mars C++ differs from some other compilers in this respect— extern data is not assumed to be in DGROUP. Although our implementation involves more work on the program- mer's part (putting __far in the right places), the control this offers makes it very worthwhile for high-performance applications. Also, most applications need less than 64KB of static data, and so need not pay a penalty for assuming that extern data is not accessible via DS.

Virtual function tables (vtbls)

The compiler generates a table of pointers to functions for each class that has virtual functions or is derived from a class with virtual functions. In the small data models (Tiny, Small, and Medium), it creates these tables in DGROUP by default. For the Compact and Large data models, it writes these tables to the code segment by default.

To further minimize use of code space in Compact and Large model programs, place vtbls in far data segments with -NV.

Segmentation options

Use the options described below to control how the compiler segments your program.

Code generation options

The options described below control how the compiler generates code.

Compiling for Windows

See Windows Prolog/Epilog Code Generation.

Templates

Internationalization Options