Compile-time sizeof conditional

2020-08-09 08:13发布

问题:

I want to define a macro if a condition involving sizeof is true and do nothing (but still compile) if it is false. If the preprocessor supported sizeof, it would look like this:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

There are some pages (e.g. http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/) which explain how to make a compile-time assertion on sizeof (and fail to compile if it fails), but I don't see a way to extend this approach to what I want.

回答1:

You just can't do it. sizeof is a compile time operator. #if and #define and preprocessor related. As the preprocessor runs BEFORE the compiler this just won't work. You may, however, be able to find an arcane compiler switch that will allow you to multi pass it (ie preprocess, pretend compile, preprocess, compile) but, in all fairness, I'd give up trying to do what you want. Its not meant to work and, simply, it doesn't.

Your best best is to set such defines as -D commands passed to the compiler. You can statically assert that the ones chosen are correct. This way you just have to set up a few defines externally for a given compile mode (eg PowerPC Release) and so on.



回答2:

The correct solution to your problem is to use the C99 standard headers:

#include <stdint.h>
#include <inttypes.h>

You only need one of the two because #include <inttypes.h> includes the material from #include <stdint.h>; however, a lot of the material in <inttypes.h> is only relevant to formatted I/O with scanf() and printf().

Given the putative condition:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

What you seem to be after is known as:

uintptr_t

That is the unsigned integer type that is big enough to hold any pointer (that is, any data pointer in the C standard; POSIX imposes an additional rule that it must also be big enough to hold function pointers too). The type uintptr_t is defined in <stdint.h>.

If you are subsequently going to be printing such values, or raw pointers, you can use the information from <inttypes.h>:

printf("Pointer = 0x%" PRIXPTR "\n", uintptr_value);
printf("Pointer = 0x%" PRIXPTR "\n", (uintptr_t)any_pointer);


回答3:

This describes how to fake compile-time assertions in C. The short version is to use switch statements:

#define COMPILE_TIME_ASSERT(pred)            \  
    switch(0){case 0:case pred:;}

If pred evaluates to 0, like a false boolean expression does in C, the compiler will throw an error.



回答4:

Assuming C99, you could use

#include <limits.h>
#include <stdint.h>

#if UINTPTR_MAX <= UINT_MAX
...

which implies sizeof (void *) <= sizeof (intptr_t) <= sizeof (int) on any sane implementation of the C language.



回答5:

Given that the other answers already explained why sizeof cannot be used with #if, let me provide a simple solution for your case (surprisingly not yet mentioned). Take a look at

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros.

It mentions several predefined __SIZEOF_XYZ__ macros that actually can be used in preprocessing phase, i.e. also in #if. Assuming unsigned int and int are of same size, your example can be done like this:

#if __SIZEOF_POINTER__ == __SIZEOF_INT__
#define POINTER_FITS_INTO_UINT
#endif


回答6:

Even though the question is tagged C rather than C++, you may find it helpful to know that C++0x defines a mechanism for static assertions which are checked by the compiler, not the preprocessor.

The Wikipedia example is particularly relevant:

static_assert (sizeof(int) <= sizeof(T), "T is not big enough!")


回答7:

Edit

Never mind, as Steve Rowe pointed out, these preprocessor values get set by sizeof as well so we just came full circle.

Since sizeof doesn't evaluate until compile time, you need to rely upon other pre-processor values. Here is how I would do it:

#include <values.h>
#if PTRBITS <= INTBITS
#  define POINTER_FITS_INTO_UINT
#endif


回答8:

You're confusing two compilation steps here. When you compile a C program, first step is the preprocessor which resolves includes, macros, any line starting by '#'. Then comes the compilation which incidentally evaluates the sizeof expressions.

These are two different binaries and you can't pass that type of information from one to another. You'll have to use system defined macros such as __i386__ or __x86_64__ if you want to figure what architecture you are on and then deduce int and pointer sizes.



回答9:

One way to make sense of this is the concept of Data model (see for example http://sourceforge.net/p/predef/wiki/DataModels/).

There are several data models including LP32 ILP32 LP64 LLP64 ILP64 and on most platforms, the cc frontend command defines the current model (e.g. _ILP32 meaning int, long and pointer are 32bit while _LP64 means long and pointer are 64bit).