Advertisement

Use of define over const

Started by July 13, 2001 02:15 PM
16 comments, last by the_grip 23 years, 7 months ago
Stoffel: Although the misuse of defines can be a burden, most people seem to know how to get it right:

#define DSP_START_ADDR    0x4000#define BUFFER_START_ADDR (DSP_START_ADDR + 0x1000)#define BUFFER_END_ADDR   (DSP_START_ADDR + 0x1FFF)int get_buf_size (){  return BUFFER_END_ADDR - BUFFER_START_ADDR;} 
Oh, I know how to get it right. So did my workmate. The person who wrote the library that supplied the header file did not . And the problem was difficult to find at best--we are talking over 2 megs of source code here, where the problem had to be found by stepping through assembly.

Again, consts do not have this problem. Those who answer "oh, it''s easy enough just not to make a mistake in the first place" must forget (or have never known) what it''s like to work with other peoples'' code on massive projects.
Advertisement
By the way, to answer the anonymous poster(s), const variables are not always "variables that can't be modified", and "expressions involving 'const' are evaluated at runtime" isn't always true either. In fact, nearly all compilers will optimise a constant variable into inline code in a finished program.

Example 1: this is in 'debug' mode, when technically no optimisations should be done (excuse the ugly IOstreams):
      58:       const int X = 10;00401493   mov         dword ptr [ebp-10h],0Ah59:       const int Y = 15;0040149A   mov         dword ptr [ebp-14h],0Fh60:61:       int z = X + Y;004014A1   mov         dword ptr [ebp-18h],19h62:63:       cout << "z:" << z;004017B8   mov         eax,dword ptr [ebp-18h]004017BB   push        eax004017BC   push        offset string "z:" (00471084)004017C1   push        offset std::cout (0047ee78)004017C6   call        @ILT+695(std::operator<<) (004012bc)004017CB   add         esp,8004017CE   mov         ecx,eax004017D0   call        @ILT+275(std::basic_ostream::operator<<) (00401118) 


Note that, although it has allocated space for X and Y (whereas #define would not), it didn't need to evaluate Z's value at runtime. Because it knows it will always be 25 (19h) at compile time, this is optimized away, even in a debug build.

Now, looking at the same code in Release build:
     ; 58   : 	const int X = 10;; 59   : 	const int Y = 15;; 60   : ; 61   : 	int z = X + Y;; 62   : ; 63   : 	cout << "z:" << z;	push	25					; 00000019H	push	OFFSET FLAT:??_C@_02HHFJ@z?3?$AA@	; `string'	push	OFFSET FLAT:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::cout	call	??6std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<	add	esp, 8	mov	ecx, eax	call	??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z ; std::basic_ostream::operator<< 

Note that it has done away with not only the constants X and Y, but also the variable Z, because the compiler knows their value and can just use a constant '25' to pass to the function.

The moral of this story is: don't worry about consuming space or runtime evaluation speed. 'const' is nearly always the best tool for the job in C++. The only reasons for using #define that I am aware of are header file inclusion guards, conditional compilation, and the more advanced preprocessor tricks such as Zipster mentioned.

Edited by - Kylotan on July 17, 2001 7:45:11 AM

Edited by - Kylotan on July 17, 2001 7:57:48 AM
The stuff is of this way. Constants and defines are usually compiled at different times. Defines are done through the preprocessor . Constants are compiled by the compiler . When defines are used like constants, there is not so much difference, but the safe type one.

But defines are a lot more powerful than this. They can be used to create macros (that's like functions but where the code is as if it were inlined, and it's inlined at precompiling time), but that was already menctioned here by Zipster.

But a point about the defines that wasn't menctioned here (I think) is that they can be used for conditional compilation. That is, you can take an entire part of the code in the compiling time off the final executable just by commenting a define.

That's the way I use it:

    #define BLA2// some codebla1();#ifdef BLA2    // more code, that's the one that could get uncompiled    bla2();#endif BLA2// a bit more of codebla3();// no more codebla4();  


If that code were compiled, it would compile every line, in the normal way.

But if I commented the #define BLA2 line, I would get a compiled program without the bla2(); line... And as it isn't never seen by the compiler but by the preprocessor, that part of the code could easily be anything uncompilable (a chapter of a book, for example) that it would act as if it were commented, so the compiler would never scream about that.

Conditional compiling is one of the more useful things of C, that's a really easy way to get multiple version of a same program compiled just by modifying a single line of the code.

Uh... I hope I've been clear, my text above is kinda messed


--DK
--H. Hernán Moraldo

Edited by - DoctorK on July 17, 2001 10:55:18 AM

Edited by - DoctorK on July 17, 2001 11:01:08 AM
--DK--H. Hernán Moraldohttp://www.hhm.com.ar/Sign up to the HHM's developers' newsletter.
Please regard that the whole purpose for introducing inline functions in C++ is for the replacement of unsafe macros!!

I agree there is no way (I know about) to avoid the # macro,
but most of the defines can be eliminated by inline functions.


PS #define BLA is not a macro, but a precompiler directive, this is the main purpose for #defines

Gr,
BoRReL
Sorry for the misinformation (I''m the Anon that posted #7 in this thread)

For the last 11 yrs, I''ve been working almost exclusively with compilers for embedded systems, and this is how the ones I used handled const. I never thought of trying it on MSVC before posting (didn''t have access to it at the time).

In our systems, the ''const'' is used a lot of times to either:

1. Specify that the data will go into ROM or,
2. Provide some protection so that someone can''t inadvertently change a location

The #2 a lot of times can be used for EEPROM locations. You can''t accidentally change them in the code without some specific manipulation (it won''t compile), but their values may change externally, which may be why the compiler doesn''t fold the constants in an equation at compile time.

I don''t know enough about console and handheld development to know if there are some similarities to the types of embedded systems I''ve worked on (HVAC & process control industry), so my previous post might have only caused confusion.
Advertisement
quote:
Original post by Anonymous Poster
Sorry for the misinformation (I''m the Anon that posted #7 in this thread)

For the last 11 yrs, I''ve been working almost exclusively with compilers for embedded systems, and this is how the ones I used handled const. I never thought of trying it on MSVC before posting (didn''t have access to it at the time).

Well, how a compiler treats constants is obviously compiler-dependent. Generally, a compiler is allowed to do whatever it wants with your code, providing the logic remains the same. So what you said wasn''t wrong, just not always the case, and not the case on MSVC.
quote:
In our systems, the ''const'' is used a lot of times to either:

1. Specify that the data will go into ROM or,
2. Provide some protection so that someone can''t inadvertently change a location

Yeah. When you think about it, it makes a lot of sense. On a desktop PC, memory is cheap, so compilers are likely to sacrifice size to gain speed, by substituting out the constants. Whereas on an embedded platform, where small code size may be important, and where there may be special provision for read-only memory, it makes sense to make use of such ROM to store constants.
quote:
The #2 a lot of times can be used for EEPROM locations. You can''t accidentally change them in the code without some specific manipulation (it won''t compile), but their values may change externally, which may be why the compiler doesn''t fold the constants in an equation at compile time.

Incidentally, if the value might change externally, it should have the ''volatile'' qualifier, which is precisely what it''s there for. A compiler should generally feel free to optimise away ''const'' variables as they are only modifiable within your program. (Although it probably still wouldn''t do so on your platform due to code size considerations.) However a ''const volatile'' variable is specified as one where your program won''t change it, but that could be changed externally. In this case, the compiler will not optimize it out, and additionally will be sure to read it each time before using its value.
quote:
If you used const instead, this wouldn''t happen.


What kind of programmer doesn''t know that when you put expressions in #define''s, you use parenthesis? That guy needs training.

This topic is closed to new replies.

Advertisement