Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

#define in C, needs explaination pls

Status
Not open for further replies.

skyrock

New Member
I am migrating from ASM to C and but kind of confused with some examples I went through. As I understand, #define makes and only makes constant? But somehow i went through some rather complicated usage of defines which I can't understand. Can someone help explain what the last four #define tries to do?

Code:
/* eemap.h */

struct CFG {
    char    c;
    int     i;
    long    l;
    double  d;
};

struct CAL {
    int scale;
    int offset;
    int slope;
};

struct LOG_ENTRY {
    long time;
    long meas;
};

typedef struct EE_MAP {
    struct CFG cfg;
    struct CAL cal;
    struct LOG_ENTRY log[1024];
} EE_MAP;

#define EE_ADDR_CFG(mbr)      (offsetof(EE_MAP, cfg) + offsetof(struct  CFG, mbr))
#define EE_ADDR_CAL(mbr)      (offsetof(EE_MAP, cal) + offsetof(struct  CAL, mbr))
#define EE_ADDR_LOG()         (offsetof(EE_MAP, log))
#define EE_ADDR_LOG_ENTRY(i)  (EE_ADDR_LOG() + ((i) * sizeof(struct LOG_ENTRY)))
 
Last edited:
#define is part of CPP the c pre processor. It runs prior to the actual compiler. Or you can think of it as the fist step in compilation.

#define in its most simple form is just text substution.

#define RAT 6​

does not define a constat RAT. Rather it replaces each case of RAT in the source by 6.


#define SKYROCK(x) anyOldText(x)​

will replace

SKYROCK(frogFace);​

with

anyOldText(frogFace);​

A more useful macro would be.

#define RADTODEG(x) ((x) * 57.29578)​

Another use is configure code. For example a LCD driver I wrote can be used on either side of a 28 pin pic. If it is on the left LATA is the data and the first 4 bits of LATC are the control signals. If it is on the right LATB is the data and the upper 4 bits of LATC is the control signals.

In my main.c file I either #define LCD_AC or LCD_BC. In the LCD header file I have
Code:
#ifdef LCD_AC
#define LCD_DATA LATA
....
#else
#define LCD_DATA LATB
#endif

Read more at
C preprocessor - Wikipedia, the free encyclopedia

EDIT: The code you posted also uses struct. Struct is a way of forming user defined data structures. This is part of the compiler and not CPP. First understand struct to understand the example. It also uses the c functions sizeof and offsetof. There is a lot going on here.
 
Last edited:
Hi there,

#defines can be used to define constants or macros. The four in your code are defining macros. A macro is something like a function.

To understand #defines and other preprocessor directives, you have to know that they are never seen by the compiler. When you compile a C program, the code is first run through a preprocessor which finds any #defines and #includes, and then replaces any instances of them in the source code with the contents of the #define or the contents of the file named by the #include.

For instance, if you do the following:

Code:
#include <stdio.h>

int abs(int n) {
    return n > 0 ? n : -n;
}

void main(void) {
    int i;

    for (i = -10; i <= 10; i++) {
        printf("%d\n", abs(i));
    }
}

. . .then the only thing that the preprocessor does is replace the #include line with the contents of the file stdio.h. abs() is a function here and is called once per loop.

However, you could also do this:

Code:
#include <stdio.h>

#define ABS(n) (n > 0 ? n : -n)

void main(void) {
    int i;

    for (i = -10; i <= 10; i++) {
	printf("%d\n", ABS(i));
    }
}

In this case, the preprocessor replaces the #include line with the contents of stdio.h, but it also replaces every instance of "ABS(n)" in the code with "(n > 0 ? n : -n)", where n can be any integer.

In the first example, a function is defined and called on each loop. In the second example, a macro is defined, and the loop is rewritten to:

Code:
    for (i = -10; i <= 10; i++) {
	printf("%d\n", (n > 0 ? n : -n));
    }

. . .before the compiler ever sees it. Macros can be useful in a lot of ways; in the above example it makes sense to use macros since the cost of including the code is lower than the cost of calling a function on each loop.

You should Google on terms like "c #define" and "c #ifdef" to learn more. The C preprocessor is a powerful tool.


Regards,

Torben

[Edit: 3v0 beat me to it. :)]
 
Last edited:
I like to use the term constant to describe constants as declared by

const int aRealConstant=42;​

and #define as text substution. It may be picking a nit but it can prevent a bit of confusion.

I prefer to use compiler constants for constants in the code because they are more trackable by debuggers.

On the other hand macro's are great for compilers that do not have #inline or do a poor job of implementing it.
 
My big rule for #defines is that they should never ever contain a semi colon. If they are a multi line define then code such as
Code:
    if(sometest)
        My#defineWithaSemicolon;
will cause a very hard to find bug. Just make it a function.

Mike.
 
I like to use the term constant to describe constants as declared by

const int aRealConstant=42;​

and #define as text substution. It may be picking a nit but it can prevent a bit of confusion.

Nope--I agree that it's a valuable distinction.

I prefer to use compiler constants for constants in the code because they are more trackable by debuggers.

On the other hand macro's are great for compilers that do not have #inline or do a poor job of implementing it.

Yes, also agreed.


Torben
 
My big rule for #defines is that they should never ever contain a semi colon. If they are a multi line define then code such as
Code:
    if(sometest)
        My#defineWithaSemicolon;
will cause a very hard to find bug. Just make it a function.

Mike.

Yes, you need to be careful with a few things like that when using macros. If I had ended my ABS() macro with a semicolon, for instance, the compiler would have choked on the code.

Other things to be careful with include scope (there is no local scope in a macro, as opposed to a function, so side effects are easy to introduce); a "recursive" macro probably won't do what you want; typos in the macro definition can manifest problems a long way from the macro definition and its use, etc.

They're handy when needed, but IMHO it's best to save them for when you know you need them and understand the possible consequences.


Just my $0.02 CDN,

Torben
 
More C #define stuff...

It is important to keep in mind that #define does not produce any code or data defintions. (Toben and Mike know this). When a #define'ed THING is used latter in the program it may result in code or data.

One of my favorite macros changes specific port bits using masking. I am sure it has been around in various forms decades.

#define SET_PORT_BITS(dest,mask,data) dest=(dest&~mask)|(data&mask)

// set the lower 4 bits of LATA to 0x02
// without changing the upper 4 bits
SET_PORT_BITS(LATA,0x0F,0x02);

// same as
LATA=(LATA&0xF0)|(LATA&0x0F);

At some point I need to look at how C18 and BoostC handle multi bit field handling within structs. Could be it is just as good or better. It should be much easier to read and debug.

Naming conventions can help a lot in understanding a program. I use NAMES_LIKE_THIS for #defines. Regular program data has namesLikeThis and functions use namesLikeThis();

I try to use only one or two macros in a program. They make debugging more difficult. The ones I use are well tested. If we had a compiler that did a decent job of #inline I would have even less use for them.

On the other hand I like to use C# like data types. I have a myTypes.h file with

Code:
#define uInt unsigned int
#define uChar unsigned char
#define byte unsigned char
...
Note that I am breaking my own rules here. uInt should be U_INT and byte BYTE ! I make this execption for data types in that I can not stand to look at U_INT
 
Thanks for the replies, you all give a good picture to my question above.
 
Last edited:
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top