-2

I've got a define in C, looks like this

#define ROW_SIZE ID_SIZE + USERNAME_SIZE + EMAIL_SIZE

It evaluates to 293 Then I do this

uint32_t num_rows = pager->file_length / ROW_SIZE;

pager->file_length is 0 for sure
The result of num_rows is 289. Even 0 / ROW_SIZE evaluates to 289.

Why is the answer to expression wrong?

Cyber Gh
  • 180
  • 3
  • 8
  • 5
    `#define ROW_SIZE ID_SIZE + USERNAME_SIZE + EMAIL_SIZE` -> `#define (ROW_SIZE ID_SIZE + USERNAME_SIZE + EMAIL_SIZE)` – Jabberwocky Aug 21 '18 at 09:29
  • 2
    This is a very common FAQ. Any beginner-level C programming book will explain how to write proper macros, in the pre-processor chapter. It will dead certain mention this particular bug with operator precedence and lack of proper parenthesis. So go read that chapter. – Lundin Aug 21 '18 at 09:30
  • 1
    Anyone know of a canonical duplicate for this? We need one! – Lundin Aug 21 '18 at 09:31
  • Always put parenthesis when defining a macro – kyriakosSt Aug 21 '18 at 09:31
  • 1
    @Lundin How about this one: [Is there a good reason for always enclosing a define in parentheses in C?](https://stackoverflow.com/questions/9081479/is-there-a-good-reason-for-always-enclosing-a-define-in-parentheses-in-c) – Andrew Henle Aug 21 '18 at 09:52
  • @AndrewHenle Seems mostly focused on if parenthesis is still needed even though there's just one pre-processor token. I think we need something more basic to use as canonical dupe. – Lundin Aug 21 '18 at 10:00
  • @Lundin Unfortunately, we killed *StackOverflow Documentation*... It would be useful in such cases – kyriakosSt Aug 21 '18 at 10:56
  • @KyrSt No, one of many reasons why we killed it because it was partially filled with crap, written by confused beginners. For a high quality FAQ, there's the [C tag wiki](https://stackoverflow.com/tags/c/info), which is maintained and only points at high quality Q&A canonical dupes. – Lundin Aug 21 '18 at 11:17

4 Answers4

3

What happens is that

uint32_t num_rows = pager->file_length / ROW_SIZE;

is expanded into

uint32_t num_rows = pager->file_length / ID_SIZE + USERNAME_SIZE + EMAIL_SIZE;

so num_rows is evaluates to 0 + ID_SIZE + USERNAME_SIZE + EMAIL_SIZE, which is not a zero. Consider adding parentheses to your define:

#define ROW_SIZE (ID_SIZE + USERNAME_SIZE + EMAIL_SIZE)
Joker_vD
  • 3,715
  • 1
  • 28
  • 42
2

Macro expansion is token-based replacement. In other words,

pager->file_length / ROW_SIZE

expands to

pager->file_length / ID_SIZE + USERNAME_SIZE + EMAIL_SIZE

I believe you agree the above is not necessarily zero, especially if USERNAME_SIZE or EMAIL_SIZE are nonzero.

To solve this, put parentheses around the expression in the macro definition:

#define ROW_SIZE (ID_SIZE + USERNAME_SIZE + EMAIL_SIZE)
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

It should be written like this:

#define ROW_SIZE (ID_SIZE + USERNAME_SIZE + EMAIL_SIZE)
asdf
  • 221
  • 1
  • 10
0

You have to remember that preprocessor macros are basically query-replaced in the source code.

If you have e.g.

uint32_t num_rows = pager->file_length / ROW_SIZE;

what the compiler sees after preprocessing is

uint32_t num_rows = pager->file_length / ID_SIZE + USERNAME_SIZE + EMAIL_SIZE;

Due to the normal operator precedence that is equal to

uint32_t num_rows = (pager->file_length / ID_SIZE) + USERNAME_SIZE + EMAIL_SIZE;

which of course is not what you expect or want. Therefore it's important to always use extra parentheses in macros, as in

#define ROW_SIZE (ID_SIZE + USERNAME_SIZE + EMAIL_SIZE)
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621