<p>In C the <a href="https://stackoverflow.com/a/4079267/16217248"><code>sizeof()</code> operator cannot be resolved in preprocessor conditions</a>. If other operators such as <code>+</code> or <code>-</code> can be (so long as the operands are constants) then what is different about <code>sizeof()</code>?</p>
<p>What technical limitations prevent <code>sizeof()</code> from being resolved at preprocess time while other operators can be and what would it take for C to permit constructs such as the following?</p>
<pre class="lang-c prettyprint-override"><code>#if sizeof(int) == 4
// Code
#endif
</code></pre>
<p><code>sizeof()</code> is compile time constant but not preprocess time constant, hence the following is valid:</p>
<pre class="lang-c prettyprint-override"><code>static char array[sizeof(int)];
</code></pre>
<p>If the compiler knows type sizes why can the preprocessor not?</p>

<types><operators><c><sizeof>
If size could be determined at compile time then why could size not be determined at preprocess time?
CC BY-SA 4.0
2023-06-08T20:08:55.797Z
In the case of C, struct padding/packing is implementation-defined and I think it depends on compiler flags in modern compilers. So, the preprocessor would have to understand those variables, which it generally doesn’t and in some cases can’t.
Bbrk24
CC BY-SA 4.0
2023-06-08T20:13:22.667Z
I’ve said this on other posts before, but a lot of the specific decisions C made were to make the compiler simpler — the language is over 50 years old at this point.
Bbrk24
CC BY-SA 4.0
2023-06-08T21:53:39.407Z
The preprocessor is entirely text-based. It knows nothing about the semantics of the files it's operating on. It can resolve a mathematical expression in an `#if`, but it cannot possibly understand what a name refers to
abel1502
CC BY-SA 4.0
2023-06-09T12:03:26.690Z
What are the close votes for here? It seems a clear on-topic question about implementing a C compiler. Insufficient research?
Bruce Adams
CC BY-SA 4.0
2023-06-09T15:28:33.783Z
@BruceAdams Other questions that are just "Why didn't C do this?" have been decided to be off-topic and were edited ([example](https://languagedesign.stackexchange.com/q/232/15)). [Relevant meta](https://languagedesign.meta.stackexchange.com/q/96/15), and I thought we had discussed this on Area51, but I can't find the post.
Bbrk24
CC BY-SA 4.0
2023-06-09T19:27:42.757Z
@Bbrk24 Your example question does not seem to be substantially less about C after editing and the meta question you link to also says questions about specific languages are on topic. Both argue against the close vote. What am I missing?
Bruce Adams
CC BY-SA 4.0
2023-06-09T19:42:22.757Z
@BruceAdams Sorry, I linked the wrong meta post. [This](https://languagedesign.meta.stackexchange.com/q/28/15) is the one I had in mind.
Bbrk24
CC BY-SA 4.0
2023-06-09T20:04:19.250Z
I see. That makes much more sense!
Bruce Adams
<p>C was designed to allow programs to be built on computers with extremely limited memory.  This made it necessary to run certain processing phases to completion before loading in code that would perform the next phase.  Preprocessing would have required keeping track of a significant number of symbols which could relatively long expansions, and would thus require a fairly large amount of &quot;data&quot; memory, but the preprocessing program itself could be fairly small and simple.  A later compilation phase would be more complicated, requiring a bigger program to perform it, but it wouldn't need to keep track of variable-length objects.</p>
<p>What is unfortunate is not that the preprocessor is limited in what it can do, but that there's no mechanism <em>other</em> than the preprocessor via which a program could include or exclude structure fields based upon information (such as object sizes) that would be available to the compiler, but not the preprocessor.</p>

CC BY-SA 4.0
2023-09-08T20:33:32.587Z
2023-09-08T20:33:32.587Z
supercat
<p>In order to evaluate the expression <code>sizeof(T)</code>, it is necessary to know what type is meant by <code>T</code>, which in general requires parsing and name resolution (at the type level) to have already been done. In fact, even recognising that <code>sizeof(T)</code> <em>is</em> an expression and that <code>T</code> <em>is</em> a type means that parsing must already have been done. The C preprocessor is run before parsing.</p>
<p>In order for the preprocessor to be able to evaluate expressions like <code>sizeof(T) == 4</code>, there would need to be a back-and-forth between the preprocessor and the compiler; the preprocessor would have to invoke the compiler to parse and evaluate these expressions, and the type declarations they refer to, before continuing to preprocess more source code.</p>
<p>This isn't <em>impossible</em> in principle, but it would be rather complex even just to specify what the correct results are supposed to be. For example, what happens when the preprocessor needs to know the size of a type, but the type's declaration itself can be different depending on another preprocessor condition? What happens if preprocessor conditions are circularly dependent?</p>
<hr />
<p>So this sort of thing is much easier to do <em>after</em> compilation, using e.g. constant folding and dead code elimination. In languages which compile to Javascript, for example, you might write something like</p>
<pre class="lang-typescript prettyprint-override"><code>const someFunction = (SOME_ARRAY.length &gt;= 4 ? function() {
    // ...
} : function() {
    // ...
});
</code></pre>
<p>Then when the condition evaluates to a compile-time constant <code>true</code> or <code>false</code>, the compiler ─ or some later build step ─ can simplify this declaration to just the one relevant function body.</p>

CC BY-SA 4.0
2023-06-08T20:31:34.010Z
2023-06-08T20:31:34.010Z
kaya3
CC BY-SA 4.0
2023-09-12T15:29:44.320Z
@Aiono You are correct... but compile-time evaluation is of course more complex to implement, and there are still limitations -- such as recursion/co-recursion leading to unsolvable problems. Nicer to use in general, though, definitely.
Matthieu M.
CC BY-SA 4.0
2023-06-12T17:24:06.553Z
I don't know much about Zig but this aspect seems like an advantage of compile time evaluation versus preprocessors. In compile time evaluation there is no expansion stage so anything that is known in compile time just works.
Aiono
# If size could be determined at compile time then why could size not be determined at preprocess time?
Posted by: username_0 on 2023-06-08
In C the sizeof() operator cannot be resolved in preprocessor conditions. If other operators such as + or - can be (so long as the operands are constants) then what is different about sizeof()?
What technical limitations prevent sizeof() from being resolved at preprocess time while other operators can be and what would it take for C to permit constructs such as the following?
#if sizeof(int) == 4
// Code
#endif

sizeof() is compile time constant but not preprocess time constant, hence the following is valid:
static char array[sizeof(int)];

If the compiler knows type sizes why can the preprocessor not?
**Tags:** types operators c sizeof
> **Comments:**
> **username_1**: In the case of C, struct padding/packing is implementation-defined and I think it depends on compiler flags in modern compilers. So, the preprocessor would have to understand those variables, which it generally doesn’t and in some cases can’t.  
> **username_1**: I’ve said this on other posts before, but a lot of the specific decisions C made were to make the compiler simpler — the language is over 50 years old at this point.  
> **username_2**: The preprocessor is entirely text-based. It knows nothing about the semantics of the files it's operating on. It can resolve a mathematical expression in an `#if`, but it cannot possibly understand what a name refers to  
> **username_3**: What are the close votes for here? It seems a clear on-topic question about implementing a C compiler. Insufficient research?  
> **username_1**: @BruceAdams Other questions that are just "Why didn't C do this?" have been decided to be off-topic and were edited ([example](https://languagedesign.stackexchange.com/q/232/15)). [Relevant meta](https://languagedesign.meta.stackexchange.com/q/96/15), and I thought we had discussed this on Area51, but I can't find the post.  
> **username_3**: @Bbrk24 Your example question does not seem to be substantially less about C after editing and the meta question you link to also says questions about specific languages are on topic. Both argue against the close vote. What am I missing?  
> **username_1**: @BruceAdams Sorry, I linked the wrong meta post. [This](https://languagedesign.meta.stackexchange.com/q/28/15) is the one I had in mind.  
> **username_3**: I see. That makes much more sense!  
---
## 2 answers
### Answer by username_5 on 2023-06-08. Score: 5 | Accepted answer
In order to evaluate the expression sizeof(T), it is necessary to know what type is meant by T, which in general requires parsing and name resolution (at the type level) to have already been done. In fact, even recognising that sizeof(T) is an expression and that T is a type means that parsing must already have been done. The C preprocessor is run before parsing.
In order for the preprocessor to be able to evaluate expressions like sizeof(T) == 4, there would need to be a back-and-forth between the preprocessor and the compiler; the preprocessor would have to invoke the compiler to parse and evaluate these expressions, and the type declarations they refer to, before continuing to preprocess more source code.
This isn't impossible in principle, but it would be rather complex even just to specify what the correct results are supposed to be. For example, what happens when the preprocessor needs to know the size of a type, but the type's declaration itself can be different depending on another preprocessor condition? What happens if preprocessor conditions are circularly dependent?

So this sort of thing is much easier to do after compilation, using e.g. constant folding and dead code elimination. In languages which compile to Javascript, for example, you might write something like
const someFunction = (SOME_ARRAY.length >= 4 ? function() {
    // ...
} : function() {
    // ...
});

Then when the condition evaluates to a compile-time constant true or false, the compiler ─ or some later build step ─ can simplify this declaration to just the one relevant function body.
> **Comments:**
> **username_7** (2023-06-12): I don't know much about Zig but this aspect seems like an advantage of compile time evaluation versus preprocessors. In compile time evaluation there is no expansion stage so anything that is known in compile time just works.  
> **username_6** (2023-09-12): @Aiono You are correct... but compile-time evaluation is of course more complex to implement, and there are still limitations -- such as recursion/co-recursion leading to unsolvable problems. Nicer to use in general, though, definitely.  
---
### Answer by username_4 on 2023-09-08. Score: 3
C was designed to allow programs to be built on computers with extremely limited memory. This made it necessary to run certain processing phases to completion before loading in code that would perform the next phase. Preprocessing would have required keeping track of a significant number of symbols which could relatively long expansions, and would thus require a fairly large amount of "data" memory, but the preprocessing program itself could be fairly small and simple. A later compilation phase would be more complicated, requiring a bigger program to perform it, but it wouldn't need to keep track of variable-length objects.

What is unfortunate is not that the preprocessor is limited in what it can do, but that there's no mechanism *other* than the preprocessor via which a program could include or exclude structure fields based upon information (such as object sizes) that would be available to the compiler, but not the preprocessor.
---
