What is the easiest way to find the sizeof a type without compiling and executing code?

前端 未结 4 1933
逝去的感伤
逝去的感伤 2021-01-20 08:04

I wrote a bash script to determine the size of gcc\'s datatypes (e.g. ./sizeof int double outputs the respective sizes of int and

相关标签:
4条回答
  • 2021-01-20 08:32

    Using nm with no code

    Just make your thing a global variable. nm can report its size.

    // getsize.c
    
    struct foo {
        char str[3];
        short s;     // expect padding galore...
        int i;
    } my_struct;
    

    Compile but don't link, then use nm:

    $ gcc -c getsize.c
    $ nm getsize.o --format=posix
    my_struct C 000000000000000c 000000000000000c
    

    Note that the last column is the size (in hex), here is how we can get it:

    $ nm test.o -P | cut -d ' ' -f 4
    000000000000000c
    
    # or in decimal
    $ printf %d 0x`nm test.o -P | cut -d ' ' -f 4`
    12
    

     

    Using objdump with no code

    If nm doesn't work for some reason, you can store the size itself in a global variable.

    Start with this C file:

    // getsize.c
    struct foo { char str[3]; short s; int i; };
    
    unsigned long my_sizeof = sizeof(struct foo);
    

    Now we have to find the value of this variable from the object file.

    $ gcc -c sizeof.c
    $ objdump -Sj .data sizeof.o
    
    test.o:     file format elf64-x86-64
    
    
    Disassembly of section .data:
    
    0000000000000000 <my_sizeof>:
       0:   0c 00 00 00 00 00 00 00                             ........
    

    Darn, little endian! You could write a script to parse this, but the following solution (assuming GCC extensions) will force it to always be big endian:

    // getsize.c
    struct foo { char str[3]; short s; int i; };
    
    struct __attribute__ ((scalar_storage_order("big-endian"))) {
        unsigned long v;
    } my_sizeof = { sizeof(struct foo) };
    

    This yields:

    0000000000000000 <my_sizeof>:
       0:   00 00 00 00 00 00 00 0c                             ........
    

    Watch out! You can't just strip out all non-hex characters because sometimes the "...." stuff on the right will be valid ASCII. But the first one should always be a .. The following command keeps things between the : and the first ..

    $ gcc -c sizeof.c
    $ objdump -Sj .data sizeof.o |
    
            sed '$!d                     # keep last line only
                 s/\s//g                 # remove tabs and spaces
                 s/.*:\([^.]*\)\..*/\1/' # only keep between : and .'
    
    000000000000000c
    
    0 讨论(0)
  • 2021-01-20 08:35

    Here are three possible solutions.

    The first one will work with any type whose size is less than 256. On my system, it takes about 0.04s (since it doesn't need headers or libraries other than the basic runtime). One downside is that it will only do one at a time, because of the small size of the output channel. Another problem is that it doesn't compensate for slow linking on some systems (notably MinGW):

    howbig() {
      gcc -x c - <<<'int main() { return sizeof ('$*'); }' && ./a.out
      echo $?
    }
    
    $ time howbig "struct { char c; union { double d; int i[3];};}" 
    24
    
    real    0m0.041s
    user    0m0.031s
    sys     0m0.014s
    
    $ time howbig unsigned long long
    8
    
    real    0m0.044s
    user    0m0.035s
    sys     0m0.009s
    

    If you wanted to be able to do larger types, you could get the size one byte at a time, at the cost of a couple more centiseconds:

    howbig2 () 
    { 
        gcc -x c - <<< 'int main(int c,char**v) {
                          return sizeof ('$*')>>(8*(**++v&3)); }' &&
        echo $((0x$(printf %02x $(./a.out 3;echo $?) $(./a.out 2;echo $?) \
                                $(./a.out 1;echo $?) $(./a.out 0;echo $?)) ))
    }
    
    $ time howbig2 struct '{double d; long long u[12];}([973])'
    101192
    
    real    0m0.054s
    user    0m0.036s
    sys     0m0.019s
    

    If you are compiling for x86, the following will probably work, although I'm not in a position to test it thoroughly on a wide variety of architectures and platforms. It avoids the link step (notoriously slow on MinGW, for example), by analyzing the compiled assembly output. (It would probably be slightly more robust to analyze the compiled object binary, but I fear that binutils on MinGW are also slow.) Even on Ubuntu, it is significantly faster:

    howbig3 () { 
      gcc -S -o - -x c - <<< 'int hb(void) { return sizeof ('$*'); }' |
      awk '$1~/movl/&&$3=="%eax"{print substr($2,2,length($2)-2)}'
    }
    
    $ time howbig3 struct '{double d; long long u[12];}([973])'
    101192
    
    real    0m0.020s
    user    0m0.017s
    sys     0m0.004s
    
    0 讨论(0)
  • 2021-01-20 08:47

    You can achieve the functionality for standard types using the GCC's preprocessor only. For standard types there are predefined macros:

    __SIZEOF_INT__
    __SIZEOF_LONG__
    __SIZEOF_LONG_LONG__
    __SIZEOF_SHORT__
    __SIZEOF_POINTER__
    __SIZEOF_FLOAT__
    __SIZEOF_DOUBLE__
    __SIZEOF_LONG_DOUBLE__
    __SIZEOF_SIZE_T__
    __SIZEOF_WCHAR_T__
    __SIZEOF_WINT_T__
    __SIZEOF_PTRDIFF_T__
    

    So, by using code like the following:

    #define TYPE_TO_CHECK __SIZEOF_INT__
    #define VAL_TO_STRING(x) #x
    #define V_TO_S(x) VAL_TO_STRING(x)
    #pragma message V_TO_S(TYPE_TO_CHECK)
    #error "terminate"
    

    you will be able to get the value of __SIZEOF_INT__ from the preprocessor itself without even starting the compilation. In your script you can define the TYPE_TO_CHECK (with -D) to whatever you need and pass it to gcc. Of course you will get some junk output, but I believe you can deal with that.

    0 讨论(0)
  • 2021-01-20 08:48

    You can use the 'negative array size' trick that autoconf (see: AC_COMPUTE_INT) uses. That way, you don't need to link or execute code. Therefore, it also works when cross compiling. e.g.,

    int n[1 - 2 * !(sizeof(double) == 8)];
    

    fails to compile if: sizeof(double) != 8

    The downside is, you might have to pass -DCHECK_SIZE=8 or something similar in the command line, since it might take more than one pass to detect an unusual value. So, I'm not sure if this will be any faster in general - but you might be able to take advantage of it.

    Edit: If you are using gcc exclusively, I think @wintermute's comment is probably the best solution.

    0 讨论(0)
提交回复
热议问题