0

I want to define several macros to calculate the size of a type. The following is an example running normally.

#include <stdio.h>

#define A sizeof(long long) / sizeof(int)
#define B 36 / A

int main(){
    printf("%zu %zu\n", A, B); // print out: 2 1
}

While it becomes strange when using SIMD vectors, for example (the definition of A)

#include <x86intrin.h>
#include <stdio.h>

#define A sizeof(__m128i) / sizeof(int)
#define B 36 / A

int main(){
    printf("%zu %zu\n", A, B); // print out 4 0
}

What's the issue?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user9985127
  • 175
  • 5
  • 4
    Hint: operator evaluation order. – Nate Eldredge Dec 05 '20 at 06:18
  • 2
    `an example running normally` If you expected `B` to be `1` in the first case, then I guess I don't understand what you thought `B` could possibly be in the second case, other than `0`. – dxiv Dec 05 '20 at 06:30

2 Answers2

3

Remember that macros are not variables: they're merely substituted tokens. So B expands to

36 / sizeof(__m128i) / sizeof(int)

Division in C and C++ associates left-to-right so this is equivalent to

(36 / sizeof(__m128i)) / sizeof(int) 

If sizeof(__m128i) is 16 and sizeof(int) is 4, then this expression is (36 / 16) / 4. Now integer division in C and C++ truncates, so 36/16 is 2, and 2/4 is 0.

For this reason, you should always parenthesize macros that expand to expressions:

#define A (sizeof(__m128i) / sizeof(int))
#define B (36 / A)

If using C++, a better solution is not to use macros at all; constants in C++ are quite fully featured, and avoid this issue.

constexpr std::size_t A = sizeof(__m128i) / sizeof(int);
constexpr std::size_t B = 36 / A;
Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 2
    `constexpr std::size_t` :) – Jarod42 Dec 05 '20 at 08:50
  • 2
    Macro replacement substitutes preprocessor tokens, not text. (The mistaken belief it substitutes text results in other questions from students not understanding why something they expect to work like text substitution does not. Let’s avoid giving people the wrong idea from the start.) – Eric Postpischil Dec 05 '20 at 12:13
0

With

#define A sizeof(__m128i) / sizeof(int)
#define B 36 / A

then the macro B will expand as 36 / sizeof(__m128i) / sizeof(int) which is evaluated as (36 / sizeof(__m128i)) / sizeof(int).

The result of the first division will be zero, and zero divided with anything is zero. Which is the result you get.

The solution, as almost always when having trouble with macros, is to add explicit parentheses in your macro definitions:

#define A (sizeof(__m128i) / sizeof(int))
#define B (36 / A)
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621