Bir önişlemci makrosunda "sizeof" u nasıl kullanabilirim?


95

sizeofÖnişlemci makrosunda a kullanmanın herhangi bir yolu var mı ?

Örneğin, yıllar içinde aşağıdakilere benzer bir şey yapmak istediğim tonlarca durum oldu:

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif

Burada tam olarak kontrol ettiğim şey tamamen uydurma - önemli olan nokta şu ki, yanlış hizalayabilecek veya yeniden hizalayabilecek bir veri yapısını değiştiren birine karşı korunmak için bu tür (boyut veya hizalama) derleme zamanı kontrolleri yapmaktan hoşlanıyorum. onları kıracak boyutta şeyler.

Söylemeye gerek yok - a'yı sizeofyukarıda açıklanan şekilde kullanabilecek gibi görünmüyorum .


Yapı sistemlerinin var olmasının tam nedeni budur.
Šimon Tóth

3
Bu, #error direktiflerinin her zaman çift tırnak içinde olması gerektiğinin tam sebebidir ("değil" nedeniyle sonlandırılmamış karakter sabiti).
Jens

1
Merhaba @ Brad. Lütfen kabul ettiğiniz cevabı boşver cevabı olarak değiştirmeyi düşünün, çünkü bu arada, şu anda kabul edilen cevap biraz eskimiş oldu.
Bodo Thiesen

@BodoThiesen Bitti.
Brad

Yanıtlar:


71

Bunu yapmanın çeşitli yolları var. Aşağıdaki parçacıklar, sizeof(someThing)eşitse hiçbir kod üretmez PAGE_SIZE; aksi takdirde bir derleme zamanı hatası üretirler.

1. C11 yolu

C11 ile başlayarak kullanabilirsiniz static_assert(gerektirir #include <assert.h>).

Kullanım:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");

2. Özel makro

sizeof(something)Beklediğiniz gibi olmadığında sadece bir derleme zamanı hatası almak istiyorsanız, aşağıdaki makroyu kullanabilirsiniz:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

Kullanım:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );

Bu makale neden işe yaradığını ayrıntılı olarak açıklamaktadır.

3. MS'ye özgü

Microsoft C ++ derleyicisinde , 2. bölümde anlatılana benzer bir numara kullanan C_ASSERT makrosunu (gerektirir #include <windows.h>) kullanabilirsiniz .

Kullanım:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);

4
...delilik bu. Neden kabul edilen cevap bu değil, @Brad (OP)?
Engineer

BUILD_BUG_ON için güzel referans.
Petr Vepřek

2
Makro, GNU'da çalışmaz gcc(4.8.4 sürümünde test edilmiştir) (Linux). At ((void)sizeof(...ile bu hataları expected identifier or '(' before 'void've expected ')' before 'sizeof'. Ancak prensip olarak size_t x = (sizeof(...bunun yerine amaçlandığı gibi çalışır. Sonucu bir şekilde "kullanmak" zorundasın. Bunun bir işlev içinde veya genel kapsamda birden çok kez çağrılmasına izin vermek için , benzer bir şey extern char _BUILD_BUG_ON_ [ (sizeof(...) ];tekrar tekrar kullanılabilir (yan etki yoktur, aslında _BUILD_BUG_ON_hiçbir yere gönderme yapmayın ).
JonBrave

Statik iddiaları 2011'den çok daha uzun süredir kullanmak bir yıl oldu.
Dan

1
@Engineer bak, delilik durdu;)
Bodo Thiesen

71

sizeofBir ön işlemci makrosunda bir " " kullanmanın bir yolu var mı ?

Hayır. Koşullu yönergeler sınırlı bir koşullu ifade kümesi alır; sizeofizin verilmeyen şeylerden biridir.

Ön işleme yönergeleri, kaynak çözümlenmeden önce (en azından kavramsal olarak) değerlendirilir, bu nedenle henüz boyutlarını elde etmek için herhangi bir tür veya değişken yoktur.

Bununla birlikte, C'de derleme zamanı iddialarını elde etmek için teknikler vardır (örneğin, bu sayfaya bakın ).


Harika makale - akıllı çözüm! Yönetici olmanız gerekse de - bunun çalışması için gerçekten C sözdizimini sınırına kadar zorladılar! : -O
Brad

1
Görünüşe göre - makalenin de söylediği gibi - şu anda Linux çekirdek kodu oluşturuyorum - ve zaten çekirdekte bir tanım var - BUILD_BUG_ON - burada çekirdek onu aşağıdaki gibi şeyler için kullanıyor: BUILD_BUG_ON (sizeof (char)! = 8)
Brad

2
@Brad BUILD_BUG_ON ve diğerleri derlenemeyecek (ve işlem sırasında belli olmayan bir hata mesajı verecek) kesinlikle yanlış kod üretir. Gerçekte #if ifadesi değildir, bu nedenle örneğin buna dayalı olarak kod bloğunu hariç tutamazsınız.
keltar

10

Bunun geç bir cevap olduğunu biliyorum, ancak Mike'ın sürümüne eklemek için, burada herhangi bir bellek ayırmayan kullandığımız bir sürüm var. Orijinal ölçü kontrolünü bulmadım, yıllar önce internette buldum ve maalesef yazara atıfta bulunamıyorum. Diğer ikisi sadece aynı fikrin uzantılarıdır.

Typedef oldukları için hiçbir şey tahsis edilmemiştir. Adında __LINE__ ile, her zaman farklı bir addır, böylece gerektiğinde kopyalanıp yapıştırılabilir. Bu, MS Visual Studio C derleyicilerinde ve GCC Arm derleyicilerinde çalışır. CodeWarrior'da çalışmıyor, CW yeniden tanımlamadan şikayet ediyor, __LINE__ ön işlemci yapısını kullanmıyor.

//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];

//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];

//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];

Bu aslında standart bir C projesi için gerçekten işe yarıyor ... Hoşuma gitti!
Ashley Duncan

1
Sıfır tahsis nedeniyle bu doğru cevap olmalıdır. Daha da iyi bir tanımla:#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
Renaud Cerrato

p__LINE__ benzersiz bir ad üretmez. Değişken olarak p__LINE__ üretir. Bir preproc makroya ihtiyacınız olacak ve sys / cdefs.h'den __CONCAT kullanmalısınız.
Coroos

9

Bu ileti dizisinin gerçekten eski olduğunu biliyorum ama ...

Çözümüm:

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];

Bu ifade sıfıra eşit olduğu sürece, iyi derler. Başka bir şey olursa orada patlar. Değişken extern'd olduğundan yer kaplamaz ve hiç kimse ona başvurmadığı sürece (ki yapmayacaktır) bağlantı hatasına neden olmaz.

İddia makrosu kadar esnek değil, ancak bunu GCC sürümümde derleyemedim ve bu olacak ... ve sanırım hemen hemen her yerde derlenecek.


6
Asla iki alt çizgi ile başlayarak kendi makrolarınızı icat etmeyin. Bu yol deliliktir ( tanımlanmamış davranış olarak da bilinir ).
Jens

Bu sayfada listelenen birçok örnek var pixelbeat.org/programming/gcc/static_assert.html
portforwardpodcast

arm gcc derleyicisi ile derlendiğinde çalışmaz. beklenen hatayı veriyor "hata: dosya kapsamında değişken olarak değiştirilmiş ' KONTROL '"
thunderbird

@Jens Haklısın ama bu tam anlamıyla bir makro değil, bu bir değişken bildirimi. Tabii ki makrolarla etkileşime girebilir.
Melebius

4

Mevcut yanıtlar, bir türün boyutuna bağlı olarak "derleme zamanı iddialarının" etkisinin nasıl elde edileceğini gösterir. Bu, bu özel durumda OP'nin ihtiyaçlarını karşılayabilir, ancak bir türün boyutuna bağlı olarak gerçekten bir ön işlemciye ihtiyaç duyduğunuz başka durumlar da vardır. İşte bunu nasıl yapacağınız:

Kendinize aşağıdaki gibi küçük bir C programı yazın:

/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }

Bunu derleyin. Favori betik dilinizde, yukarıdaki C programını çalıştıran ve çıktısını yakalayan bir betik yazın. Bir C başlık dosyası oluşturmak için bu çıktıyı kullanın. Örneğin, Ruby kullanıyorsanız şöyle görünebilir:

sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER

Ardından, Makefile dosyanıza veya başka bir derleme betiğine bir kural ekleyin; bu, oluşturmak için yukarıdaki komut dosyasını çalıştırmasını sağlar sizes.h.

sizes.hÖnişlemci koşullarını boyutlara göre kullanmanız gereken her yere ekleyin .

Bitti!

(Hiç ./configure && makebir program oluşturmak için yazdınız mı ? configureKomut dosyaları temelde yukarıdakiler gibidir ...)


"autoconf" gibi araçlar kullandığınızda da benzer bir şey.
Alexander Stohr

4

Ya sonraki makro:

/* 
 * Simple compile time assertion.
 * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
 */
#define CT_ASSERT(exp, message_identifier) \
    struct compile_time_assertion { \
        char message_identifier : 8 + !(exp); \
    }

Örneğin yorumda MSVC şöyle bir şey söyler:

test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits

1
Bunu bir #ifönişlemci yönergesinde kullanamayacağınız için bu sorunun cevabı değildir .
cmaster -

1

Bu tartışma için bir referans olarak, bazı derleyicilerin sizeof () ar bir ön işlemci süresi aldığını bildiriyorum.

JamesMcNellis'in yanıtı doğrudur, ancak bazı derleyiciler bu sınırlamadan geçer (bu muhtemelen katı ansi c'yi ihlal eder).

Bunun bir örneği olarak, IAR C-derleyicisine (muhtemelen profesyonel mikro denetleyici / gömülü programlama için en önde gelen) başvuruyorum.


Bundan emin misin? IAR , derleyicilerinin ön işleme zamanında değerlendirmeye izin vermeyen ISO C90 ve C99 standartlarına uygun olduğunu iddia ediyor sizeof. sizeofyalnızca bir tanımlayıcı olarak ele alınmalıdır.
Keith Thompson

6
1998'de comp.std.c haber grubundan biri şöyle yazdı: " #if (sizeof(int) == 8)(bazı derleyicilerde) gibi şeylerin gerçekten çalıştığı günler güzeldi ." Cevap: "Benim zamanımdan önce gelmiş olmalı." Dennis Ritchie'den geldi.
Keith Thompson

Geç cevap verdiğim için özür dilerim ... Evet, eminim, 8/16/32 bit mikrodenetleyiciler, Renesas derleyicileri (hem R8 hem de RX) için derlenmiş kodların çalışma örneklerim var.
graziano governatori

Aslında, "katı" ISO C'yi zorunlu
kılmak

standart yasaklamadığı sürece standardın ihlali değildir. o zaman buna nadir ve standart dışı bir özellik derim - bu nedenle derleyicinin bağımsızlığını ve platform taşınabilirliğini korumak adına normal durumlarda bundan kaçınacaksınız.
Alexander Stohr

1

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) çalışabilir


Bu ilginç bir çözümdür ancak türlerle değil, yalnızca tanımlanmış değişkenlerle çalışır. #define SIZEOF_TYPE(x) (((x*)0) + 1)
Türle

7
Çalışmaz çünkü sonucunu bir #ifkoşul içinde hala kullanamazsınız . Üzerinde fayda sağlamaz sizeof(x).
interjay

1

C11'de _Static_assertanahtar kelime eklenir. Şu şekilde kullanılabilir:

_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")

0

Taşınabilir c ++ kodumda ( http://www.starmessagesoftware.com/cpcclibrary/ ) bazı yapılarımın veya sınıflarımın boyutlarına güvenli bir koruma koymak istedim.

Önişlemcinin bir hata atması için bir yol bulmak yerine (burada belirtildiği gibi sizeof () ile çalışamaz), burada derleyicinin bir hata atmasına neden olan bir çözüm buldum. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99

Derleyicimde (xcode) bir hata atmasını sağlamak için bu kodu uyarlamam gerekiyordu:

static union
{
    char   int8_t_incorrect[sizeof(  int8_t) == 1 ? 1: -1];
    char  uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
    char  int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
    char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
    char  int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
    char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
};

2
Bunların "-1" hiçbir zaman 0xFFFF… FF olarak yorumlanmayacağından ve programınızın tüm adreslenebilir belleği istemesine neden olmayacağından emin misiniz?
Anton Samsonov

0

Bahsedilen makroları denedikten sonra, bu parça istenen sonucu veriyor gibi görünüyor ( t.h):

#include <sys/cdefs.h>
#define STATIC_ASSERT(condition) typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(int) == 42);

Koşu cc -E t.h:

# 1 "t.h"
...
# 2 "t.h" 2

typedef char _static_assert_3[ (sizeof(int) == 4) ? 1 : -1];
typedef char _static_assert_4[ (sizeof(int) == 42) ? 1 : -1];

Koşu cc -o t.o t.h:

% cc -o t.o t.h
t.h:4:1: error: '_static_assert_4' declared as an array with a negative size
STATIC_ASSERT(sizeof(int) == 42);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.h:2:84: note: expanded from macro 'STATIC_ASSERT'
  ...typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
                                                       ^~~~~~~~~~~~~~~~~~~~
1 error generated.

Sonuçta 42 her şeye cevap değil ...


0

Derleme zamanında veri yapılarının boyutunu kısıtlamalarına göre kontrol etmek için bu numarayı kullandım.

#if defined(__GNUC__)
{ char c1[sizeof(x)-MAX_SIZEOF_X-1]; } // brakets limit c1's scope
#else
{ char c1[sizeof(x)-MAX_SIZEOF_X]; }   
#endif

X'in boyutu, MAX_SIZEOF_X sınırından büyük veya ona eşitse, gcc 'dizi boyutu çok büyük' ​​hatasıyla şikayet eder. VC ++, C2148 ('dizinin toplam boyutu 0x7fffffff bayt'ı geçmemelidir') veya C4266 'sabit boyutlu 0 dizisini ayıramaz' hatasını verir.

İki tanım gereklidir çünkü gcc sıfır boyutlu bir dizinin bu şekilde tanımlanmasına izin verir (sizeof x - n).


-10

sizeofOperatör önişlemcisinden için kullanılamaz, ancak aktarabilirsiniz sizeofderleyici ve çalışma zamanı içinde durumunu kontrol edin:

#define elem_t double

#define compiler_size(x) sizeof(x)

elem_t n;
if (compiler_size(elem_t) == sizeof(int)) {
    printf("%d",(int)n);
} else {
    printf("%lf",(double)n);
}

13
Zaten kabul edilmiş cevapta nasıl gelişir? Hangi amacı tanımlamakcompiler_size hizmet eder? Örneğiniz ne göstermeye çalışıyor?
ugoren
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.