91

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.

altendky
  • 4,176
  • 4
  • 29
  • 39
  • What is the motivation? – Ed Heal Jan 07 '14 at 18:59
  • 4
    To know the size of a multi-tiered struct without digging into the map file. – altendky Jan 07 '14 at 19:01
  • 1
    Well - the #warning statement is handled by the preprocessor before the proper compiler has even started - so I don't think this is possible. I guess writing a small test program, which is invoked as custom step in the build process, is a solution. Good luck. – user422005 Jan 07 '14 at 19:43
  • 1
    What do you mean by "far from ... fast"? Static assertions are tested at compile-time (and so is `sizeof` evaluated at compile-time). – mafso Jan 07 '14 at 20:03
  • And there is nothing like a call to `sizeof`. It's an operator, not a function. – mafso Jan 07 '14 at 20:04
  • @user422005 I will at least agree that I never should have expected #warning to work as it is a precompiler command, not a compiler directive. – altendky Jan 07 '14 at 20:05
  • @mafso "far from ... fast" refers to writing the pile of static asserts. Such as, MY_STATIC_ASSERT(sizeof(MyStruct) > 100). If I have to do a search changing 100 to 200 then to 300 then 250, then 275, then... it is far from a fast process. Also, I have removed the reference to a sizeof() call. – altendky Jan 07 '14 at 20:09
  • do you need the exact size or just a ballpark figure ? – Chaim Geretz Jan 07 '14 at 20:37
  • 2
    If you have a C++ compiler for your target, you might check it using http://stackoverflow.com/questions/2008398/is-it-possible-to-print-out-the-size-of-a-c-class-at-compile-time – nos Jan 07 '14 at 20:57
  • @Chaim Geretz exact in some cases but ballpark would have been sufficient today. – altendky Jan 07 '14 at 21:43
  • @nos I could probably add something to our standard Makefile to ease the use of the C++ approach. I may end up going that route if Filipe is correct that what I want is not possible. – altendky Jan 07 '14 at 21:45
  • @altendky Another approach if you can generate elf files with debugging info in them is to use the `pahole` tool on the final executable or object files. (Or even using objdump --dwarf) – nos Jan 07 '14 at 22:28
  • @altendky: you could speed this up a tiny bit by using a pile of #if #else MY_STATIC_ASSERTS to get a ballpark warning. – Chaim Geretz Jan 08 '14 at 15:56
  • @nos I had tried `objdump --dwarf` on a small struct and it seemed inaccurate. I should revisit to see what I did wrong. – altendky Jan 08 '14 at 16:15
  • @ChaimGeretz The static assert works by `typedef`'ing an array type with a positive (assert passed) or negative (assert failed) size. This does not do anything in regard to the preprocessor so `#if` does not work with it. That said, what I did do was create a bunch of asserts operating on a `#define` that I could point to the struct of interest. – altendky Jan 08 '14 at 16:20

14 Answers14

120

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

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.

IgNite
  • 652
  • 2
  • 6
  • 17
  • 2
    Visiting this yet a year later, I found the above solution for gcc no longer works (gcc 4.4.2). After a bit more searching, I found that http://stackoverflow.com/questions/21001044/is-there-a-way-to-determine-a-member-offset-at-compile-time (using an array that is to large, with `-Wframe-larger-than`) still works (you have to scroll down to the accepted answer, as it's not on top for some reason...). – blackghost Mar 28 '17 at 13:47
  • 1
    I had luck with a recent Clang version, but your link also worked so double good. – Michael Dorgan Jun 23 '17 at 18:18
  • 1
    I like this solution! Anyway, can someone please remove the last quotation mark in the printf in the kaboom_print function? This just gives me an additional error which I am not interested in. – ola1olsson Jan 04 '18 at 15:26
  • 1
    This actually saved me some time today. Only strange thing is, a static assert fails due to size not being X... Doing this to check what the compiler thinks the size is... give me X :P – inquam Oct 12 '19 at 13:26
  • This was the tip of the day :-), also I can announce that it works with the C2000 compiler from Texas Intruments (TI) – Otzen Mar 17 '23 at 15:56
28

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 <stdio.h>
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.

3) 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.

4) optionally add a case 16 to verify it e.g.

#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:
    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.

JavaMan
  • 4,954
  • 4
  • 41
  • 69
  • Unfortunately doesn't work in my older version of gcc 4.2.0, it just says 'duplicate case value' without printing the value. – eresonance Mar 09 '17 at 13:30
  • some general methods to print calculated int values during compilation: https://stackoverflow.com/questions/28852574/how-to-print-result-of-a-compile-time-calculation-in-c/44183676#44183676 – JavaMan Jun 08 '17 at 08:46
  • this was the only one that worked with gcc in c for me – Michael Oct 15 '18 at 17:28
  • edited to use the trick on gcc that only prints "duplicate case value" without printing the actual case value. – JavaMan Jun 02 '20 at 19:29
  • Why isn't this the top answer? Works for me with MSVS C++ – Tatiana Racheva Aug 04 '21 at 21:33
17

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]'

Ruslan
  • 18,162
  • 8
  • 67
  • 136
9

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.

4

@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] "

dsptech
  • 41
  • 4
4

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]'
urish
  • 8,943
  • 8
  • 54
  • 75
  • 2
    Note, this actually compiled for me in VS 2019. But changing "bla" to a number (e.g. `4`) worked. – default Sep 14 '21 at 14:42
3

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).

3
//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
ChrisZZ
  • 1,521
  • 2
  • 17
  • 24
  • Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Dima Kozhevin Jul 29 '20 at 06:52
  • Downvoted since the question is about C, not C++ – Michael Ansolis May 02 '23 at 23:20
1

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.

jws
  • 2,171
  • 19
  • 30
  • Gcc complains about unhandled cases in switches. So if you had some invalid entry like case 1: and no default, gcc should complain case 8 not handled. – Atilla Filiz Jul 09 '19 at 13:44
1

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
1

My gcc wasn't giving any of the results with array indexes in them, so I came up with another way based on some responses here to get the actual answer.

int a;
a = 1/ (sizeof(struct page5_data) & 0x0001);
a = 1/ (sizeof(struct page5_data) & 0x0002);
a = 1/ (sizeof(struct page5_data) & 0x0004);
a = 1/ (sizeof(struct page5_data) & 0x0008);
a = 1/ (sizeof(struct page5_data) & 0x0010);
a = 1/ (sizeof(struct page5_data) & 0x0020);
a = 1/ (sizeof(struct page5_data) & 0x0040);
a = 1/ (sizeof(struct page5_data) & 0x0080);
a = 1/ (sizeof(struct page5_data) & 0x0100);
a = 1/ (sizeof(struct page5_data) & 0x0200);
a = 1/ (sizeof(struct page5_data) & 0x0400);
a = 1/ (sizeof(struct page5_data) & 0x0800);
a = 1/ (sizeof(struct page5_data) & 0x1000);
a = 1/ (sizeof(struct page5_data) & 0x2000);
a = 1/ (sizeof(struct page5_data) & 0x4000);
a = 1/ (sizeof(struct page5_data) & 0x8000);
(void)a;

Gives:

test.c:115:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0001); ^

test.c:116:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0002); ^

test.c:125:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0400); ^

test.c:126:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0800); ^

test.c:127:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x1000); ^

test.c:128:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x2000); ^

test.c:129:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x4000); ^

test.c:130:7: error: division by zero [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x8000); ^

This tells us the zero bits of a 16bit number, the others must be '1'. So we can determine the value is:

0000 0011 1111 1100 = 0x03fc

0

A much easier way is to do this using gdb. If you're able to compile your .elf with debugging symbols.

Example:

In my test.c file, I declare

typedef struct {
  int a;
  int b;
  char d;
  long e[10];
} my_struct_t;

I compile it using gcc

gcc test.c -o app.elf -g

I run

gdb app.elf

And without running the executable, you can do

gdb app.elf
(gdb) ptype /o my_struct_t

type = struct {
/*    0      |     4 */    int a;
/*    4      |     4 */    int b;
/*    8      |     1 */    char d;
/* XXX  7-byte hole  */
/*   16      |    80 */    long e[10];

/* total size (bytes):   96 */
}

You can also print the result of the sizeof function in gdb

(gdb) p sizeof(my_struct_t)
$1 = 96

Since you don't need to run the executable, The .elf can even be the product of a cross-compilation (as long as you use your toolchain's gdb or gdb-multiarch).

-1

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

JavaMan
  • 4,954
  • 4
  • 41
  • 69
-5

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.

Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • 1
    I already have a static assert macro which successfully triggers a compile time error based on a sizeof(MyStruct) call so it is false that a program must be executed to know the size of a custom struct. The only thing I am missing is a compiler (as you point out, not a precompiler) command to print the value. – altendky Jan 07 '14 at 20:00
  • I didn't say a program must be executed to know the size of a custom struct - of course the compiler knows it at some point. What I said is that you have no way of *asking* the compiler to dump it during compilation, so your only choice is to execute a program that does that. Although you can compare it to hard-coded values, there is no instruction you can give it to print the size. – Filipe Gonçalves Jan 07 '14 at 20:04
  • "There is really nothing you can do to know the size of a custom struct without writing a program and executing it."??? how did you know for sure? See my answer – JavaMan May 09 '16 at 10:45