How can I print the result of sizeof() at compile time in C?

CSizeofCompile Time

C Problem Overview


How can I print the result of sizeof() at compile time in C?

For now I am using a static assert (home brewed based on other web resources) to compare the sizeof() result to various constants. While this works... it is far from elegant or fast. I can also create an instance of the variable/struct and look in the map file but this is also less elegant and fast than a direct call/command/operator. Further, this is an embedded project using multiple cross-compilers... so building and loading a sample program to the target and then reading out a value is even more of a hassle than either of the above.

In my case (old GCC), #warning sizeof(MyStruct) does not actually interpret sizeof() before printing the warning.

C Solutions


Solution 1 - C

I was mucking around looking for similar functionality when I stumbled on this:

https://stackoverflow.com/questions/2008398/is-it-possible-to-print-out-the-size-of-a-c-class-at-compile-time

Which gave me the idea for this:

char (*__kaboom)[sizeof( YourTypeHere )] = 1;

Which results in the following warning in VS2015:

warning C4047: 'initializing': 'DWORD (*)[88]' differs in levels of indirection from 'int'

where 88 in this case would be the size you're looking for.

Super hacky, but it does the trick. Probably a couple years too late, but hopefully this will be useful to someone.

I haven't had a chance to try with gcc or clang yet, but I'll try to confirm whether or not it works if someone doesn't get to it before me.

Edit: Works out of the box for clang 3.6

The only trick I could get to work for GCC was abusing -Wformat and having the macro define a function like the following:

void kaboom_print( void )
{
    printf( "%d", __kaboom );
}

Which will give you a warning like:

...blah blah blah... argument 2 has type 'char (*)[88]'

Slightly more gross than the original suggestion, but maybe someone who knows gcc a bit better can think of a better warning to abuse.

Solution 2 - C

Duplicate case constant is a trick that is guaranteed to work IN ALL C COMPILERS regardless of how each of them reports error. For Visual C++, it is simple:

struct X {
	int a,b;
	int c[10];
};
int _tmain(int argc, _TCHAR* argv[])
{
	int dummy;
	
	switch (dummy) {
	case sizeof(X):
	case sizeof(X):
		break;
	}
	return 0;
}

Compilation result:

 ------ Build started: Project: cpptest, Configuration: Debug Win32 ------
 cpptest.cpp c:\work\cpptest\cpptest\cpptest.cpp(29): error C2196: case value '48' already used
 ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

So sizeof the struct X is 48

EDITED (3jun2020): For gcc or any other compilers that only print "duplicate case value", I use this trick to narrow down the value:

  1. add a case value 1==2 (to represent false)

  2. by trial and error, narrow down the value e.g. I try to guess the sizeof(X) is >16:

    #include typedef struct _X { int a; char b[10]; } X; int main() { printf("Hello World");

     int dummy=0   ;
     switch (dummy) {
     case  1==2:
     case sizeof( X)>16:
     //case 16:
     break;
     }
     return 0;
    

    }

result:

main.c: In function ‘main’:
main.c:14:5: error: duplicate case value
     case sizeof( X)>16:
     ^~~~
main.c:13:5: error: previously used here
     case  1==2:

so it is false, i.e. sizeof(X)<=16.

  1. repeat with some other sensible values. e.g. try to guess that it is 16 i.e. sizeof(X)==16. If it doesn't complain about duplicate case value. Then the expression is true.

  2. optionally add a case 16 to verify it e.g.

    #include typedef struct _X { int a; char b[10]; } X; int main() { printf("Hello World");

     int dummy=0   ;
     switch (dummy) {
    // case  1==2:
     case sizeof( X):
     case 16:
     break;
     }
     return 0;
    

    }

result

main.c: In function ‘main’:
main.c:15:5: error: duplicate case value
     case 16:
     ^~~~
main.c:14:5: error: previously used here
     case sizeof( X):

confirming that sizeof(X) is 16.

Alternatively, it is observed that gcc can report multiple duplicates, so this trick is possible for making multiple guesses on a single pass:

#include <stdio.h>
typedef struct _X {
    int a;
    char b[10];
} X;
int main()
{
    printf("Hello World");

    int dummy=0   ;
    switch (dummy) {
    case  1==2: //represents false
    case 1==1: //represents true
    case sizeof( X)>10:
    case sizeof( X)>12:
    case sizeof( X)>14:
    case sizeof( X)>16:
    case  sizeof( X)==16:
    //case 16:
    break;
    }
    return 0;
}

result

main.c: In function ‘main’:
main.c:14:5: error: duplicate case value
     case sizeof( X)>10:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:15:5: error: duplicate case value
     case sizeof( X)>12:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:16:5: error: duplicate case value
     case sizeof( X)>14:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:17:5: error: duplicate case value
     case sizeof( X)>16:
     ^~~~
main.c:12:5: error: previously used here
     case  1==2:
     ^~~~
main.c:18:5: error: duplicate case value
     case  sizeof( X)==16:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~

suggesting the sizeof(X) is >10, >12, >14 but is not >16. The ==16 is added as a final guess.

Solution 3 - C

The following way, which works in GCC, Clang, MSVC and more, even in older versions, is based on failed conversion of a function parameter from pointer to array to a scalar type. The compilers do print size of the array, so you can get the value from the output. Works both in C and C++ mode.

Example code to find out sizeof(long) (play with it online):

char checker(int);
char checkSizeOfInt[sizeof(long)]={checker(&checkSizeOfInt)};

Examples of relevant output:

  • GCC 4.4.7

> <source>:1: note: expected 'int' but argument is of type 'char (*)[8]'

  • clang 3.0.0

> <source>:1:6: note: candidate function not viable: no known conversion from 'char (*)[8]' to 'int' for 1st argument;

  • MSVC 19.14

> <source>(2): warning C4047: 'function': 'int' differs in levels of indirection from 'char (*)[4]'

Solution 4 - C

One more way (that actually works):

char __foo[sizeof(MyStruct) + 1] = {[sizeof(MyStruct)] = ""};

Works with old'ish gcc 5.x. Yields an error like this:

a.c:8:54: error: initializer element is not computable at load time
a.c:8:54: note: (near initialization for 'a[8]')

p.s. obviously, this one is (very) gcc specific. All other methods weren't working for me.

Solution 5 - C

Quick and simple solution that worked for me (GCC):

(char[sizeof(long long)])"bla";

This results in an error message that reveals the size of long long:

ISO C++ forbids casting to an array type 'char [8]'

Solution 6 - C

I stumbled upon a solution similar to Bakhazard's great solution, and this one produces a much less verbose warning, so you may find it useful:

char (*__fail)(void)[sizeof(uint64_t)] = 1;

This produces the error message

Function cannot return array type 'char [8]'

This was tested with the latest version of clang(1).

Solution 7 - C

@jws nice idea!. Howewer, sizeof(xxx) is a constant expression (except VLA, https://en.cppreference.com/w/c/language/sizeof), so the sizeof operator should work even in the case selection:

enum e1 {dummy=-1};
enum e1 ev;
switch (ev) {
    case sizeof(myType):;
    break;
    default:;
}

.. it work in my GCC: "..\WinThreads.c:18:9: warning: case value '4' not in enumerated type 'enum e1' [-Wswitch] "

Solution 8 - C

//main.cpp
#include <cstddef>
template <std::size_t x>
struct show_size;

void foo()
{
    show_size<sizeof(my_type)>();//!!please change `my_type` to your expected
}

int main()
{
    return 0;
}

You can compile this pretty simple code, and during its pre-compilation stage, the compiler will give error, in which the sizeof(my_type) will give concrete value. e.g.:

g++ main.cpp

Solution 9 - C

Though this isn't exactly at compile time, it is before runtime, so it could still be relevant for some people.

You can define an array like so:

uint8_t __some_distinct_name[sizeof(YourTypeHere)];

And then, after compilation, get the size from the object file:

$ nm -td -S your_object_file |       # list symbols and their sizes, in decimal
  grep ' __some_distinct_name$' |    # select the right one
  cut -d' ' -f2 |                    # grab the size field
  xargs printf "Your type is %d B\n" # print

Solution 10 - C

My gcc C compiler refuses to print the size using any of the above solutions. I inverted the logic to inject compiler warnings for what size it is not.

enum e
{
    X = sizeof(struct mystruct)
};

void foo()
{
    static enum e ev;

    switch (ev)
    {
    case 0:
    case 4:
    case 8:
    case 12:
    case 16:
    case 20:
        break;
    }
}

Then I have to look through the warnings for the missing number.

warning: case value '0' not in enumerated type 'e' [-Wswitch]
warning: case value '4' not in enumerated type 'e' [-Wswitch]
warning: case value '12' not in enumerated type 'e' [-Wswitch]
warning: case value '16' not in enumerated type 'e' [-Wswitch]
warning: case value '20' not in enumerated type 'e' [-Wswitch]

So then my struct size is 8.

My packing is 4.

Meh... it's an option.

Solution 11 - C

This is a generic solution for any C compilers.

I've realized that if our aim is knowing the value of a sizeof() instead of printing out its value, then we just need to evaluate a few compile time sizeof(X)>?? expressions to narrow down the value.

The trick is to produce compile time errors when the expressions evaluate to false(zero) or true (non-zero).

Many standard C constructs can achieve our goal. The duplicate case value trick i described separately is one of them. Another one is through test for division by zero in an initializer which the compiler evaluates at compile time. For example, to get the size of X:

struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;

compile with a few lines:

#include <stdio.h>
struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;
int r2=1/(sizeof(X)<170);
int r3=1/(sizeof(X)<100);
int r4=1/(sizeof(X)<80);
int r5=1/(sizeof(X)<60);
int main()
{
   return 0;
}

result

main.c:17:9: warning: division by zero [-Wdiv-by-zero]
 int r3=1/(sizeof(X)<100);
         ^
main.c:17:8: error: initializer element is not constant
 int r3=1/(sizeof(X)<100);
        ^
main.c:18:9: warning: division by zero [-Wdiv-by-zero]
 int r4=1/(sizeof(X)<80);
         ^
main.c:18:8: error: initializer element is not constant
 int r4=1/(sizeof(X)<80);
        ^
main.c:19:9: warning: division by zero [-Wdiv-by-zero]
 int r5=1/(sizeof(X)<60);
         ^
main.c:19:8: error: initializer element is not constant
 int r5=1/(sizeof(X)<60);
        ^

implying sizeof(X)<170 is true (non-zero) but sizeof(X)<100 is false (causing division by zero at compile time). Then we can get the actual value by repeating the test with some other values. e.g

#include <stdio.h>
struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;
int r2=1/(sizeof(X)<140);
int r3=1/(sizeof(X)<137);
int r4=1/(sizeof(X)<136);
int r5=1/(sizeof(X)!=136);

int main()
{
    return 0;
}

result

main.c:18:9: warning: division by zero [-Wdiv-by-zero]
 int r4=1/(sizeof(X)<136);
         ^
main.c:18:8: error: initializer element is not constant
 int r4=1/(sizeof(X)<136);
        ^
main.c:19:9: warning: division by zero [-Wdiv-by-zero]
 int r5=1/(sizeof(X)!=136);
         ^
main.c:19:8: error: initializer element is not constant
 int r5=1/(sizeof(X)!=136);
        ^

Hence we know sizeof(X)==136.

Alternatively, by using the ?: operator, we can make use of more C language constructs that are evaluated at compile time. Visual C++ example using array declaration:

#include "stdafx.h"
struct X {
  int a;
  char b[30];
  double d;
  float f[20];
};
int a1[sizeof(X)<130?-1:1];
int a2[sizeof(X)<120?1:-1];
int a3[sizeof(X)==128?-1:1];

int _tmain(int argc, _TCHAR* argv[]){
  return 0;
}

result

1>------ Build started: Project: cpptest, Configuration: Release Win32 ------
1>  cpptest.cpp
1>cpptest.cpp(11): error C2118: negative subscript
1>cpptest.cpp(12): error C2118: negative subscript
1>cpptest.cpp(13): error C2118: negative subscript
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

implying the sizeof(X) is <130, not <120, and equals to 128

Solution 12 - C

You can't do this, not with structures. The preprocessor is invoked before compilation takes place, so there isn't even the concept of structure; you can't evaluate the size of something that doesn't exist / wasn't defined. The preprocessor does tokenize a translation unit, but it does so only for the purpose of locating macro invocation.

The closest thing you can have is to rely on some implementation-defined macros that evaluate to the size of built-in types. In gcc, you can find those with:

gcc -dM -E - </dev/null | grep -i size

Which in my system printed:

#define __SIZE_MAX__ 18446744073709551615UL
#define __SIZEOF_INT__ 4
#define __SIZEOF_POINTER__ 8
#define __SIZEOF_LONG__ 8
#define __SIZEOF_LONG_DOUBLE__ 16
#define __SIZEOF_SIZE_T__ 8
#define __SIZEOF_WINT_T__ 4
#define __SIZE_TYPE__ long unsigned int
#define __SIZEOF_PTRDIFF_T__ 8
#define __SIZEOF_FLOAT__ 4
#define __SIZEOF_SHORT__ 2
#define __SIZEOF_INT128__ 16
#define __SIZEOF_WCHAR_T__ 4
#define __SIZEOF_DOUBLE__ 8
#define __SIZEOF_LONG_LONG__ 8

There is really nothing you can do to know the size of a custom struct without writing a program and executing it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionaltendkyView Question on Stackoverflow
Solution 1 - Cuser5896643View Answer on Stackoverflow
Solution 2 - CJavaManView Answer on Stackoverflow
Solution 3 - CRuslanView Answer on Stackoverflow
Solution 4 - CKonstantin StupnikView Answer on Stackoverflow
Solution 5 - CurishView Answer on Stackoverflow
Solution 6 - CEHYPERCHICKENView Answer on Stackoverflow
Solution 7 - CdsptechView Answer on Stackoverflow
Solution 8 - CChrisZZView Answer on Stackoverflow
Solution 9 - Cuser10746164View Answer on Stackoverflow
Solution 10 - CjwsView Answer on Stackoverflow
Solution 11 - CJavaManView Answer on Stackoverflow
Solution 12 - CFilipe GonçalvesView Answer on Stackoverflow