#define vs const

  • (2 Pages)
  • +
  • 1
  • 2

15 Replies - 5866 Views - Last Post: 16 October 2011 - 12:43 AM

#1 Jacic  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 54
  • Joined: 15-June 10

#define vs const

Posted 01 August 2011 - 01:53 PM

Hi everyone. I was wondering what specific advantages and disadvantages const and #define give when coding.

Obviously, #define is a little simpler.
#define WHATEVER 42
const int WHATEVER = 42;


I think using #define takes no space to store the value, as it is replaced before compilation.

Some things are impossible to do with a #define, however.
#define ONE 1
#define TWO ONE + ONE


That would be easy using constants.
const int ONE = 1;
const int TWO = ONE + ONE;


So the rule of thumb is: 'if its incredibly simple, use #define, otherwise const', right?

Anything else that I'm missing?

Is This A Good Question/Topic? 0
  • +

Replies To: #define vs const

#2 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 651
  • View blog
  • Posts: 2,225
  • Joined: 31-December 10

Re: #define vs const

Posted 01 August 2011 - 01:58 PM

No the rule is don't use the pre-processor, use the const construct. There is no type-checking during pre-processing, there is however type-checking for consts.
Was This Post Helpful? 1
  • +
  • -

#3 anonymouscodder  Icon User is offline

  • member icon

Reputation: 126
  • View blog
  • Posts: 710
  • Joined: 01-January 10

Re: #define vs const

Posted 01 August 2011 - 02:00 PM

const respects scope and type, and the errors generated by the compiler are better.

In matter of performance I guess it's the same, one it's resolved before compilation but the other are really simply to optimize at compile time. For me it's a matter of organization which one to choose.
Was This Post Helpful? 1
  • +
  • -

#4 Jacic  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 54
  • Joined: 15-June 10

Re: #define vs const

Posted 01 August 2011 - 02:01 PM

Alright, good coding practices are important. Thanks for the info.
Was This Post Helpful? 0
  • +
  • -

#5 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: #define vs const

Posted 01 August 2011 - 03:09 PM

First of all lets deal with this:

Quote

I think using #define takes no space to store the value, as it is replaced before compilation.
-- This is more or less correct BUT it implies that the same is not true for a const. This is not generally true however. The compiler can (and usually does) just slip in the value of the const into the code where you use it -- except with the const you get type safety.

Now I said: "more or less true" because the number does get "stored" inline in the code. With integers this is normally ok because it saves us having to load a value by reference into a register (we can use immediate or a simple mov) and this is generally better for caching. However, there are times when it is less efficient for example in floating point operations which need to get pushed into special registers. SO -- sometimes it is better to store the value in 1 place rather than to repeat the value over and over around the code. When you use a constant you give the compiler a chance to make this decision during compile time (note however that if your turn on optimization the compiler will generally do this for even #define floating point values and literals... but it is something to think about).

That is not to say that #define constants have no use in code -- they do -- but the value generally involves really advanced or deep thinking. For example const values can not be used within other pre-processor logic. So for example one might establish a DEBUG_LEVEL constant and then use its value to determine what kind of debugging information gets displayed.

There are also Preprocessor-meta programming techniques such as advanced loops utilizing the #include directive that might use counters etc.

So -- in general it is just better to stick to C++'s type system for constants!
Was This Post Helpful? 3
  • +
  • -

#6 CreamDelight  Icon User is offline

  • D.I.C Head

Reputation: 18
  • View blog
  • Posts: 90
  • Joined: 04-July 11

Re: #define vs const

Posted 01 August 2011 - 10:02 PM

I second those posts above that using C++ data types is much better for it checks before compiling, but i just want to clarify this code below..

View PostJacic, on 01 August 2011 - 01:53 PM, said:

Some things are impossible to do with a #define, however.
#define ONE 1
#define TWO ONE + ONE


That would be easy using constants.
const int ONE = 1;
const int TWO = ONE + ONE;


Actually you can do this with macros..
so for example in your statement above, i think you were trying to use it as an adder..
A macro like this can do the job..

#include <stdio.h>
#define ADD(a, B)/> a + b

void main()
{
        int x = 11;
	printf("%d\n\n", ADD(x, 6));
}



Macros also supports variable so i think it does the job.. ^^

But then again, stick with const types.. ^^
Was This Post Helpful? 1
  • +
  • -

#7 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: #define vs const

Posted 02 August 2011 - 03:20 AM

#define ONE 1
#define TWO ONE + ONE

Funnily that works fine. It gets resolved as:
#define TWO 1 + 1

Was This Post Helpful? 0
  • +
  • -

#8 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: #define vs const

Posted 02 August 2011 - 05:26 AM

*
POPULAR

@CreamDelight -- No. Macros do NOT work like that. Macros are not "variables" that is they do not vary. The C preprocessor is a declarative language that does string substitution. When you do this:

#include <stdio.h>
#define ADD(a, b ) a + b

int main()
{
    int x = 11;
    printf("%d\n\n", ADD(x, 6));
    return 0;
}


and then run it past the preprocessor you get this:

<contents of stdio.h>

int main()
{
    int x = 11;
    printf("%d\n\n", x + 6);
    return 0;
}



All that happens when expanding ADD is the value passed for a is substituted in to the macro and the value of b is substituted in so for:

ADD(x, 6) you get the code: x + 6


Try this little program:
#include <stdio.h>
#define ADD(a, b ) a + b

int main()
{
    int x = 11;
    printf("%d\n\n", 10 * ADD(x, 6));
    return 0;
}



you might expect the output to be 10 * (11 + 6) = 170 right: but when you run the program you get 116 -- because the macro just pasted in the string of code: "x + 6" so this program is really:

printf("%d\n\n", 10 * 11 + 6);

and now operator* has higher precedence and is evaluated first, then the addition so you get 110 + 6 or 116.

another example from above is:

#define ONE 1
#define TWO ONE + ONE
...

printf("%d\n\n", TWO*10); //line becomes: printf("%d\n\n", 1 + 1*10);


The output is 11 rather than 20! because the preprocessor did not evaluate the value TWO it only did a substitution of the macro ONE into the the macro TWO and then when you use TWO it just substituted in the value 1 + 1 so the expression became 1 + 1*10.

Macros DO NOT evaluate expressions. Macros do NOT have variables because nothing varies - everything in the preprocessor language is done with string replacement. That is not to say that the preprocessor does not have a fairly bright programming environment -- you can do all kinds of neat things such as iteration and conditional logic -- for examples see these two snippets:

Selecting a language to compile for -- uses the preprocessor to to select messages in different languages.

Preprocessor Initialize Arrays -- uses iteration in the preprocessor to initialize arrays.

Advanced Preprocessor - a topic where I talk about XMacros - a technique for using a table to generate code structures such as enums and arrays of strings or case-statements etc.

Of course to really see the power of the preprocessor realized you need a library of functionality. See Boost::Preprocessor to see what can really be done. The library has for loops, while loops, lists, sequences, arrays -- it is nearly a full programming environment all built atop the declarative language of the preprocessor.

BUT -- as neat as I find macros -- they are dangerous because there is no type checking, no syntax checking (i.e. the preprocessor does not know C or C++ and will willing produce invalid code) and often macros hide syntax errors or subtle logic errors.

There is a reason why the convention is to name Macros in ALL_UPPER_CASE -- its to remind and warn programmers that what they are using is a macro and it may not work the same as a function or exactly be the constant they were hoping for.

Macros are generally designed to work in specific kinds of situations and generally cause trouble when that information is skewed between developers.
Was This Post Helpful? 9
  • +
  • -

#9 Jacic  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 54
  • Joined: 15-June 10

Re: #define vs const

Posted 02 August 2011 - 01:30 PM

@NickDMax Ah. I get it now. Thanks for your help.
Was This Post Helpful? 0
  • +
  • -

#10 GWatt  Icon User is offline

  • member icon

Reputation: 264
  • View blog
  • Posts: 3,059
  • Joined: 01-December 05

Re: #define vs const

Posted 02 August 2011 - 02:08 PM

Just in case anyone wants to see examples of fairly complex/dangerous macros, here are some that are used in one of my university's lower level systems intro classes.
#define Full(c) (c != -1)
#define Empty(c) (c == -1)

#define CONCAT2(arg1, arg2) arg1 ## arg2
#define CONCAT(arg1, arg2) CONCAT2(arg1, arg2)

#define Begin() static void *pc = 0; if (pc) goto *(pc);

#define Return(x) \
do { \
        pc = CONCAT(&&_L, __LINE__); \
        return x; \
        CONCAT( _L, __LINE__) :; \
} while (0)

#define Finish() return 0;

#define Send(ch, v) while(Full(ch) ) Return(1); ch = v

#define Receive(ch, v) while(Empty(ch) ) Return(1); v = ch; ch = -1



What this macro suite does is imitate multithreading on a single core, single thread ARM chip. The reason it's done this way is to preserve timing while receiving a signal before we get to handling interrupts.
Was This Post Helpful? 0
  • +
  • -

#11 CreamDelight  Icon User is offline

  • D.I.C Head

Reputation: 18
  • View blog
  • Posts: 90
  • Joined: 04-July 11

Re: #define vs const

Posted 02 August 2011 - 06:51 PM

View PostNickDMax, on 02 August 2011 - 05:26 AM, said:

@CreamDelight -- No. Macros do NOT work like that. Macros are not "variables" that is they do not vary. The C preprocessor is a declarative language that does string substitution. When you do this:

Macros DO NOT evaluate expressions. Macros do NOT have variables because nothing varies - everything in the preprocessor language is done with string replacement. That is not to say that the preprocessor does not have a fairly bright programming environment -- you can do all kinds of neat things such as iteration and conditional logic -- for examples see these two snippets:


Ahh okay.. I get it..
I thought what I was doing were macros..
So how do you call something like that??
like this..
#define ADD(a, B)/> a + b

.. I thought macros were that simple.. heha..
BTW.. thanks for that explanation.
Was This Post Helpful? 0
  • +
  • -

#12 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: #define vs const

Posted 03 August 2011 - 07:38 AM

I think you are asking how to make a macro that can take two numeric arguments and add them?

that is: ADD(3, 2) will substitute in 5?

I see two patterns that could work.
#1 you could use tables. Basically encoding an addition table. Nice thing about this method is that you could probably write a program to generate the macros needed (heck I think a little regex would work).

the big down side is that you have to generate a VERY large table for this to work beyond anything simple. For example a 10x10 table would be 100 elements and would only let you add numbers between 0-9 a 256x256 table would need 65536 entries and may cause problems for the precompiler... so this is may be a simple way but probably not the best.

Spoiler


#2 you could use a counting loop. This may require far fewer but still a lot of macros. There are different ways you could do this depending upon how much "infrastructure" you build. The Boost::Preprocessor library for example has macros to test for equality and tuples to hold arguments etc. so you could probably easily build some kind of for loop.

I would probably implement such this using something similar to my initialize arrays snippet building a structure like:



ADD(5, 3) becomes
ADD_1(ADD_1(ADD_1(5)))

then the ADD_1 macro just needs a table that says something like
#define ADD_1_0 1
#define ADD_1_1 2
#define ADD_1_2 3
#define ADD_1_3 4 
#define ADD_1_4 5 
#define ADD_1_5 6
#define ADD_1_6 7
#define ADD_1_7 8
#define ADD_1_8 9
#define ADD_1_9 10
#define ADD_1_10 11
#define ADD_1_11 12
#define ADD_1_12 13 
#define ADD_1_13 14
#define ADD_1_14 15 
#define ADD_1_15 16 
#define ADD_1_16 17
#define ADD_1_17 18
#define ADD_1_18 19
#define ADD_1_19 20

#define ADD_1_X(n) ADD_1_ ## n
#define ADD_1(n) ADD_1_X(n)

ADD_1(ADD_1(ADD_1(5)))



Since macro arguments are completely expanded before being substituted (if the argument is not used with # or ## anyway) we get:

ADD_1(ADD_1(ADD_1(5))) becomes 
ADD_1(ADD_1(ADD_1_X(5))) becomes 
ADD_1(ADD_1(ADD_1_5)) becomes 
ADD_1(ADD_1(6)) becomes
ADD_1(ADD_1_X(6)) becomes
ADD_1(ADD_1_6) becomes
ADD_1(7) becomes
ADD_1_X(7) becomes
ADD_1_7 becomes
8


So why did I need the macro ADD_1_X? because arguments to a macro are not expanded when used with the ## (concatenation operator) - so to ensure that they DID get expanded I needed to hide the concatenation operator.

Spoiler


The nice thing about counting is that you can do a lot more with less. With the little version of ADD in the spoiler tags there I can not only add the number 0-9 but if I place the higher number in the first argument I can do any addition whose sum is less than 21.

Learning to program in the preprocessor is a fun exercise because you really have to re-think how you approach problems. The can be a lot of typing (and/or copy/paste) but you learn real quick to use RegEx or some kind of code generation (even the preprocessor) to generate your code for you. Think about that for a second: you use macros to help you write macros... is that like meta-meta programming?

The PROBLEM with learning to program in the preprocessor is that it really is mostly mental masturbation because there really is little NEED for advanced macros. i.e. you hardly ever use this stuff for all the same reasons you should not be using macros as constants.
Was This Post Helpful? 0
  • +
  • -

#13 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: #define vs const

Posted 03 August 2011 - 08:18 AM

So the "proper" way to add constants at run time is to just add them. expressions involving constants are evaluated at compile time.

When you have: cout << 5 + 6 << endl; the compiler does the addition for you.

to abstract more advanced calculations you can use template-metaprogramming OR with C++11 you can use constexpr which lets you to make constant expression functions (which use to require TMP):

So in GCC you can do:
#include <iostream>

//requores GCC with c++0x support turned on... vs2010 does not implment constexpr yet.
constexpr int fib(int n) {
    return (n < 2) ? 1 : fib(n-2) + fib(n-1);
}


using namespace std;

int main() {
    cout << fib(0) << endl;
    cout << fib(1) << endl;
    cout << fib(2) << endl;
    cout << fib(3) << endl;
    cout << fib(4) << endl;
    cout << fib(5) << endl;
    cout << fib(6) << endl;
    cout << fib(7) << endl;
    cout << fib(8) << endl;
    cout << fib(9) << endl;
    cout << fib(10) << endl;
    int n = 11;
    cout << fib(n) << endl; //executed at run time! (well... in theory)
    return 0;
}


And this is the same as writing:
int main() {
    cout << 1 << endl;
    cout << 1 << endl;
    cout << 2 << endl;
    cout << 3 << endl;
    cout << 5 << endl;
    cout << 8 << endl;
    cout << 13 << endl;
    cout << 21 << endl;
    cout << 34 << endl;
    cout << 55 << endl;
    cout << 89 << endl;
    int n = 11;
    cout << fib(n) << endl; //note the compiler may substitute the constant value of 11 in and
// evaluate the expression at compile time... but the point is that constexpr functions can be used
// with runtime or compiletime code.
    return 0;
}
because the constexpr function is evaluated at compile time.

In visual studio 2010 which does not implement this keyword yet you still have to use TMP so you can do something like:
#include <iostream>

using namespace std;

template<int N>
struct Fib {
    enum {
        value = Fib<N-2>::value + Fib<N-1>::value,
    };
};

template<>
struct Fib<0> {
    enum {
        value = 1,
    };
};

template<>
struct Fib<1> {
    enum {
        value = 1,
    };
};

int main() {
    cout << Fib<1>::value << endl;
    cout << Fib<2>::value << endl;
    cout << Fib<3>::value << endl;
    cout << Fib<4>::value << endl;
    cout << Fib<5>::value << endl;
    cout << Fib<6>::value << endl;
    cout << Fib<7>::value << endl;
    cout << Fib<8>::value << endl;
    cout << Fib<9>::value << endl;
    cout << Fib<10>::value << endl;
    return 0;
}

Of course this goes crazy and will not compile if you try Fib<-1>::value.
If you are like me, you might ask: Why didn't you use the ?: operator to check for < 2 like you did int he constexpr? -- because in TMP compile time the compiler evaluates both the true and false conditions of the ?: operator (it needs to verify they evaluate to the same type) so the "false" condition in N < 2 ? 1 : Fib<N-2>::value + Fib<N-1>::value would evaluate even if the value is not used.
Was This Post Helpful? 0
  • +
  • -

#14 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: #define vs const

Posted 03 August 2011 - 10:58 AM

yummy TMP,i whooped up an integer square root for this, took me a bit but i got it :)

#include <iostream>

template<int n, int x = 1, int k = 0>
class Sqrt {
public:
	enum {
		value = Sqrt<n, (x + (n/x))/2, k + 1>::value
	};
};

template<int n, int x>
class Sqrt<n, x, 20> {
public:
	enum {
		value = (x + (n/x))/2
	};
};

int main() {
	std::cout<<Sqrt<49>::value;
}



it uses a place holder(k), if k gets to 20 then the recursion stops. so the precision can be adjusted if needed. im working on how to make the precision arbitrary.

edit:
20 seems to work preety good though, it may even be overkill. std::cout<<Sqrt<46340*46340>::value; prints 46340 :) but 46341*46341 is bigger than an integer can hold :)

edit2: 18 is all that is needed, 20 is over kill by 2 :)

This post has been edited by ishkabible: 03 August 2011 - 11:06 AM

Was This Post Helpful? 2
  • +
  • -

#15 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: #define vs const

Posted 03 August 2011 - 11:30 AM

neat! Now do it in the preprocessor!
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2