簡體   English   中英

如何在 C 的編譯時打印 sizeof() 的結果?

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

如何在 C 的編譯時打印 sizeof() 的結果?

現在我正在使用 static 斷言(基於其他 web 資源自制)將 sizeof() 結果與各種常量進行比較。 雖然這有效......但它遠非優雅或快速。 我還可以創建變量/結構的實例並查看 map 文件,但這也不如直接調用/命令/運算符優雅和快速。 此外,這是一個使用多個交叉編譯器的嵌入式項目......因此構建示例程序並將其加載到目標然后讀取一個值比上述任何一個都更麻煩。

在我的例子中(舊 GCC), #warning sizeof(MyStruct)在打印警告之前實際上並不解釋 sizeof() 。

當我偶然發現這個時,我正在四處尋找類似的功能:

是否可以在編譯時打印出 C++ 類的大小?

這給了我這個想法:

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

這導致在 VS2015 中出現以下警告:

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

在這種情況下,88 將是您要查找的大小。

超級hacky,但它確實有效。 可能晚了幾年,但希望這對某人有用。

我還沒有機會嘗試使用 gcc 或 clang,但如果有人在我之前沒有得到它,我會嘗試確認它是否有效。

編輯:開箱即用,適用於 clang 3.6

我可以為 GCC 工作的唯一技巧是濫用-Wformat並讓宏定義如下函數:

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

這會給你一個警告,如:

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

比最初的建議略粗,但也許對 gcc 有更好了解的人可以想到更好的濫用警告。

重復case常量是一種保證在所有 C 編譯器中都能工作的技巧,無論它們如何報告錯誤。 對於 Visual C++,這很簡單:

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;
}

編譯結果:

 ------ 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 ==========

所以 struct X 的大小是 48

編輯 (3jun2020):對於 gcc 或任何其他只打印“重復大小寫值”的編譯器,我使用這個技巧來縮小值:

1)添加一個case值1==2(代表false)

2) 通過反復試驗,縮小值范圍,例如我嘗試猜測sizeof(X)是 >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;
}

結果:

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:

所以它是錯誤的,即 sizeof(X)<=16。

3)用其他一些合理的值重復。 例如,嘗試猜測它是 16,即sizeof(X)==16 如果它不抱怨重復的大小寫值。 那么表達式為真。

4) 可選地添加一個case 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):
    case 16:
    break;
    }
    return 0;
}

結果

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

確認 sizeof(X) 是 16。

或者,觀察到 gcc 可以報告多個重復項,因此可以使用此技巧在一次傳遞中進行多次猜測:

#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;
}

結果

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:
     ^~~~

表明sizeof(X)是 >10、>12、>14 但不是 >16。 ==16 被添加為最后的猜測。

以下方法適用於 GCC、Clang、MSVC 等,甚至在舊版本中也適用,它基於函數參數從指針到數組到標量類型的失敗轉換。 編譯器會打印數組的大小,因此您可以從輸出中獲取值。 適用於 C 和 C++ 模式。

找出sizeof(long)示例代碼(在線玩):

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

相關輸出示例:

  • 海灣合作委員會 4.4.7

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

  • 叮當 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]'

另一種方式(實際上有效):

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

適用於舊的 gcc 5.x。 產生這樣的錯誤:

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

ps 顯然,這是(非常)gcc 特定的。 所有其他方法都不適合我。

我偶然發現了一個類似於Bakhazard偉大解決方案的解決方案,這個解決方案產生的警告要少得多,所以你可能會發現它很有用:

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

這會產生錯誤消息

Function cannot return array type 'char [8]'

這是用最新版本的clang(1)測試過的。

@jws 好主意!。 但是, sizeof(xxx) 是一個常量表達式(VLA 除外, https: //en.cppreference.com/w/c/language/sizeof ),因此即使在案例選擇中, sizeof 運算符也應該起作用:

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

.. 它在我的 GCC 中工作:“..\\WinThreads.c:18:9: 警告:case value '4' not in enumerated type 'enum e1' [-Wswitch]”

//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;
}

你可以編譯這個非常簡單的代碼,在它的預編譯階段,編譯器會給出錯誤,其中sizeof(my_type)會給出具體的值。 例如:

g++ main.cpp

雖然這不完全是在編譯時,它在運行時之前,所以它仍然可能與某些人相關。

您可以像這樣定義一個數組:

uint8_t __some_distinct_name[sizeof(YourTypeHere)];

然后,編譯后,從目標文件中獲取大小:

$ 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

對我有用的快速簡單的解決方案(GCC):

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

這會導致顯示long long大小的錯誤消息:

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

我的 gcc 沒有給出任何帶有數組索引的結果,因此我根據此處的一些響應想出了另一種方法來獲得實際答案。

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;

給出:

test.c:115:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0001); ^

test.c:116:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0002); ^

test.c:125:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0400); ^

test.c:126:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x0800); ^

test.c:127:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x1000); ^

test.c:128:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x2000); ^

test.c:129:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x4000); ^

test.c:130:7: 錯誤:除以零 [-Werror=div-by-zero] a = 1/ (sizeof(struct page5_data) & 0x8000); ^

這告訴我們 16 位數字的零位,其他位必須為“1”。 所以我們可以確定的值為:

0000 0011 1111 1100 = 0x03fc

我的 gcc C 編譯器拒絕使用上述任何解決方案打印大小。 我顛倒了邏輯以注入編譯器警告,說明它不是什么大小。

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;
    }
}

然后我必須查看丟失號碼的警告。

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]

那么我的結構大小是 8。

我的包裝是 4。

嗯……這是一個選擇。

一個更簡單的方法是使用 gdb 來做到這一點。 如果您能夠使用調試符號編譯 your.elf。

例子:

在我的 test.c 文件中,我聲明

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

我使用 gcc 編譯它

gcc test.c -o app.elf -g

我跑

gdb app.elf

並且無需運行可執行文件,您可以執行

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 */
}

也可以在 gdb 中打印 function 大小的結果

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

由於您不需要運行可執行文件,The.elf 甚至可以是交叉編譯的產物(只要您使用工具鏈的 gdb 或 gdb-multiarch)。

這是任何 C 編譯器的通用解決方案。

我已經意識到,如果我們的目標是知道sizeof()的值而不是打印出它的值,那么我們只需要評估一些編譯時間sizeof(X)>?? 表達式來縮小值。

訣竅是當表達式計算為false (零)或true (非零)時產生編譯時錯誤。

許多標准的 C 結構都可以實現我們的目標。 我單獨描述的重復case值技巧就是其中之一。 另一種方法是通過在編譯器在編譯時評估的初始化程序中測試被零除。 例如,要獲取 X 的大小:

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

用幾行編譯:

#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;
}

結果

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);
        ^

暗示sizeof(X)<170true (非零)但sizeof(X)<100false (導致在編譯時被零除)。 然后我們可以通過用其他一些值重復測試來獲得實際值。 例如

#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;
}

結果

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);
        ^

因此我們知道sizeof(X)==136

或者,通過使用?:運算符,我們可以使用更多在編譯時計算的 C 語言結構。 使用數組聲明的 Visual C++ 示例:

#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;
}

結果

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 ==========

暗示sizeof(X) <130,而不是 <120,等於 128

你不能這樣做,不能用結構。 預處理器在編譯之前被調用,所以甚至沒有結構的概念; 您無法評估不存在/未定義的東西的大小。 預處理器確實標記了一個翻譯單元,但它這樣做只是為了定位宏調用。

您可以擁有的最接近的事情是依賴一些實現定義的宏,這些宏計算內置類型的大小。 在 gcc 中,你可以找到那些:

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

在我的系統中打印:

#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

如果不編寫程序並執行它,您就無法知道自定義結構的大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM