C'de statik iddia


Yanıtlar:


91

C11 standardı _Static_assertanahtar kelimeyi ekler .

Bu, gcc-4.6'dan beri uygulanmaktadır :

İlk yuvanın sabit bir integral ifade olması gerekir. İkinci yuva, uzun ( _Static_assert(0, L"assertion of doom!")) olabilen sabit bir dizedir .

Bunun clang'ın son sürümlerinde de uygulandığına dikkat etmeliyim.


4
[... gcc tarafından, clang tarafından gerçeklenmiş gibi görünüyor ...] ;-)' nin C11 standardının bir parçası olduğu ve C11'i destekleyen herhangi bir derleyicinin buna sahip olacağı konusunda daha iddialı olabilirsiniz _Static_assert.
PP

1
Bu, dosya kapsamında kullanılabilir mi (herhangi bir işlevin dışında)? Çünkü error: expected declaration specifiers or '...' before 'sizeof'satır için static_assert( sizeof(int) == sizeof(long int), "Error!); alıyorum (bu arada C ++ değil C kullanıyorum)
user10607

@ user10607 Bunun işe yaramadığına şaşırdım .. Bekleyin, hata dizenizin sonunda bir alıntı eksik. Onu tak ve geri dön. Bu benim için gcc-4.9'da çalışıyor: _Static_assert( sizeof(int) == sizeof(long int), "Error!");Macine'imde hatayı alıyorum.
emsr

Ubuntu'da gcc 4.8.2 kullanıyorum. Eksik alıntı bir yorum yazım hatasıydı (kodum vardı). Bu, birkaç başlık içerdikten sonra dosyadaki ilk satırdır. Derleyici bana tamamen aynı iki hata veriyor: error: expected declaration specifiers or '...' before 'sizeof'VE error: expected declaration specifiers or '...' before string constant( "Error!"dizgeye atıfta bulunuyor ) (ayrıca: -std = c11 ile derliyorum. Bildirimi bir işlevin içine koyarken her şey iyi çalışıyor (beklendiği gibi başarısız oluyor ve başarılı oluyor)
user10607

2
@ user10607 Komut satırında -std = gnu11 belirtmem gerekiyordu. 4.8 ile 4.8 arasında bir fark olacağına gerçekten şaşırdım. Sadece tek satırlık bir kaynağım var. Ayrıca _Static_assertC ++ ish değil C standardını kullandım static_assert. Static_assert makrosunu almak için "#include <assert.h> gerekir.
emsr

93

Bu, işlev ve işlev dışı kapsamda çalışır (ancak yapıların, birliklerin içinde değil).

  1. Derleme zamanı beyanı eşleştirilemezse, GCC tarafından neredeyse anlaşılır bir mesaj üretilir. sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. Makro, typedef için benzersiz bir ad oluşturmak üzere değiştirilebilir veya değiştirilmelidir (yani adın __LINE__sonunda birleştirin static_assert_...)

  3. Üçlü bir kod yerine, bu #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]paslı olde cc65 (6502 cpu için) derleyicisinde bile işe yarayan kullanılabilir .

GÜNCELLEME: Tamlık adına, işte sürüm__LINE__

UPDATE2: GCC'ye özel kod

GCC 4.3 (sanırım) "hata" ve "uyarı" işlev özelliklerini tanıttı. Bu özniteliğe sahip bir işleve yapılan çağrı, ölü kodun ortadan kaldırılmasıyla (veya diğer önlemlerle) ortadan kaldırılamazsa, bir hata veya uyarı üretilir. Bu, kullanıcı tanımlı hata açıklamaları ile derleme zamanı iddiaları yapmak için kullanılabilir. Kukla bir işleve başvurmadan ad alanı kapsamında nasıl kullanılabileceklerini belirlemeye devam ediyor:

Ve şöyle görünüyor:


1
Visual Studio'da değişken adından bahsetmeden sadece "Negatif alt simge" yazıyor ...
szx

Nordic Mainframe - cevabınızdaki 3. seçenek clang üzerinde çalışmıyor.
Elazar

1
Son (GCC 4.3 + -specific) çözümle ilgili olarak: Bu çok güçlüdür, çünkü optimize edicinin anlayabileceği her şeyi kontrol edebilir, ancak optimizasyon etkinleştirilmezse başarısız olur. Ancak minimum optimizasyon düzeyi ( -Og) bunun çalışması için genellikle yeterli olabilir ve hata ayıklamayı engellememelidir. __OPTIMIZE__(Ve __GNUC__) tanımlanmamışsa , statik iddiayı bir işlemsiz veya çalışma zamanı iddiası yapmak düşünülebilir .
Søren Løvborg

SATIR sürümlü Kod parçacığında (GÜNCELLEME: Bütünlük uğruna, işte `LINE) sürüm, derleme sırasında, satırdaki hatalar (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), bu da bir tane daha eklenerek düzeltilebilir aşağıdaki gibi seviye: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Onay: ## L "");
sundar

__LINE__Gcc 4.1.1'deki sürüme benzer bir şey kullanıyorum ... iki farklı başlık aynı numaralı satırda bir tane olduğunda ara sıra rahatsızlık duyuyorum !
MM

10

cl

Sorunun açıkça gcc'den bahsettiğini biliyorum, ancak burada tamlık için Microsoft derleyicileri için bir ince ayar var.

İkna etmiyor typedef olumsuz boyutlu diziyi kullanarak cl iyi hata tükürmek için. Sadece diyor error C2118: negative subscript. Sıfır genişlikli bir bit alanı bu açıdan daha iyidir. Bu, bir yapının typedeffing'i içerdiğinden, gerçekten benzersiz tür adları kullanmamız gerekir. __LINE__hardalı kesmez - COMPILE_TIME_ASSERT()aynı satırda bir başlık ve bir kaynak dosyaya sahip olmak mümkündür ve derlemeniz bozulur. __COUNTER__kurtarmaya geliyor (ve 4.3'ten beri gcc'de bulunuyor).

Şimdi

altında clverir:

C2149 hatası: 'static_assertion_failed_use_another_compiler_luke': adlandırılmış bit alanı sıfır genişliğe sahip olamaz

Gcc ayrıca anlaşılır bir mesaj verir:

hata: 'static_assertion_failed_use_another_compiler_luke' bit alanı için sıfır genişlik


4

Gönderen Vikipedi :


15
Gerçek kaynağa bağlarsanız
Matt Joiner

Gcc 4.6'da çalışmaz - "durum etiketi bir tamsayı sabitine indirgenmez" diyor. Bir anlamı var.
Liosan

Her iki muhtemelen şimdi üzerinde waaay hareket ettik ama (bkz kendi yazma sona erdi cevabımı ). Bana yardım etmek için @MattJoiner bağlantınızı kullandım
Hashbrown

Rahatsız olursanız, sizin için işe yarayıp yaramadığını bana bildirin, @Liosan. C ++ 'yı daha yeni araştırmaya başladım, bu yüzden partiye geç geldim
Hashbrown

Visual C ++ 'a gelince, 2010'dan beri static_assert yerleşiktir ve hem c ++ hem de c modlarında çalışır. Ancak, yerleşik c99 _Static_assert içermez.
ddbug

3

Ben ediyorum DEĞİL bir kullanarak çözüm kullanarak tavsiye typedef:

typedefAnahtar kelimeli dizi bildiriminin derleme zamanında değerlendirilmesi garanti EDİLMEZ. Örneğin, blok kapsamındaki aşağıdaki kod derlenecektir:

Bunun yerine bunu tavsiye ederim (C99'da):

staticAnahtar sözcük nedeniyle dizi derleme zamanında tanımlanacaktır. Bu iddianın yalnızca CONDderleme zamanında değerlendirilenlerle çalışacağını unutmayın . Değişkenlere atanan değerler gibi bellekteki değerlere dayalı koşullarla çalışmaz (yani derleme başarısız olur).


4
Bu işe yarayacak olsa da bellek gereksinimlerinizi de artıracaktır.
sherrellbc

1
hata: 'static_assertion_INVALID_CHAR_SIZE' tanımlanmış ancak kullanılmamış [-Werror = kullanılmayan-değişken]
Alex

2

STATIC_ASSERT () makrosu ile birlikte kullanılıyorsa __LINE__, bir .c dosyasındaki bir giriş ile bir başlık dosyasındaki farklı bir giriş arasındaki satır numarası çatışmalarını dahil ederek önlemek mümkündür __INCLUDE_LEVEL__.

Örneğin :


1

Klasik yöntem bir dizi kullanmaktır:

Bu işe yarar çünkü eğer iddia doğruysa dizinin boyutu 1'dir ve geçerlidir, ancak yanlışsa -1'in boyutu bir derleme hatası verir.

Çoğu derleyici, değişkenin adını gösterecek ve iddia hakkında nihai yorumlar bırakabileceğiniz kodun sağ bölümünü gösterecektir.


Bunu genel bir #define STATIC_ASSERT()makroya sarmak ve genel örneklerinizden daha genel örnekler ve örnek derleyici çıktısı STATIC_ASSERT()sağlamak size çok daha fazla olumlu oy verecek ve bu tekniğin daha mantıklı olacağını düşünüyorum.
Gabriel Staples

Ben katılmıyorum Derleyici düşünce makrolarını görür ve daha kafa karıştırıcı bir mesaj verir.
Paolo.Bolzoni

1

Perl'den, özellikle perl.h3455 numaralı satır ( <assert.h>önceden dahil edilmiştir):

Varsa static_assert(nereden <assert.h>), kullanılır. Aksi takdirde, koşul yanlışsa, negatif boyutlu bir bit alanı bildirilir ve bu da derlemenin başarısız olmasına neden olur.

STMT_START/ STMT_ENDGenişleyen makrolardır do/ while (0)sırasıyla.


1

Çünkü:

  1. _Static_assert() artık C'nin tüm sürümleri için gcc'de tanımlanmıştır ve
  2. static_assert() C ++ 11 ve sonrasında tanımlanmıştır

Bu STATIC_ASSERT()nedenle aşağıdaki basit makro burada çalışır:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) veya üstü
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (standart belirtilmemiş)

Aşağıdaki STATIC_ASSERTgibi tanımlayın :

Şimdi kullan:

Örnekler:

Gcc 4.8.4 kullanılarak Ubuntu'da test edilmiştir:

Örnek 1: iyi gccçıktı (yani: STATIC_ASSERT()kodlar çalışıyor, ancak koşul yanlıştı, derleme zamanı iddiasına neden oluyordu):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 'main'
işlevinde static_assert.c: 78: 38: error: static assertion fail: "(1> 2) başarısız"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") başarısız")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^

Örnek 2: iyi g++ -std=c++11çıktı (yani: STATIC_ASSERT()kodlar çalışıyor, ancak koşul yanlıştı, derleme zamanı iddiasına neden oluyordu):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: 'int main ()'
static_assert.c: 74: 32: hata: statik ispat başarısız oldu: (1> 2) başarısız
#define _Static_assert static_assert / * static_assertC ++ 11 veya sonraki bir sürümün parçasıdır * /
^
static_assert.c: 78: 38: not: '_Static_assert' makrosunun genişletilmesinde
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") başarısız")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^

Örnek 3: başarısız C ++ çıkışı (yani, C ++ 11'den önce bir C ++ sürümü kullanıldığından, onay kodu hiç düzgün çalışmıyor )

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: uyarı: tanımlayıcı 'static_assert' C ++ 11 [-Wc ++ 0x-uyumlu]
STATIC_ASSERT (1> 2 );
^
static_assert.c: 'int main ()'
static_assert.c: 78: 99: hata: 'static_assert' bu kapsamda bildirilmedi
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) başarısız ")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^

Tam test sonuçları burada:

İlişkili:

  1. Makroya iletilen türleri kontrol etmek için static_assert kullanın [kendi cevabım]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Makroya iletilen türleri kontrol etmek için static_assert kullanın
  3. Bir makroya geçirilen parametre türlerini kontrol etmek için C'de statik assert nasıl kullanılır

1
İçinde bir static_assertmakro varken neden bu kadar karmaşık assert.h?
Hoşçakal SE

@KamiKaze, sorunuza şaşırdım, çünkü cevabımı gerçekten okumamışsınız gibi görünüyor? Cevabımın 2. satırı her şeyi söylüyor: "static_assert () C ++ 11 ve sonrasında tanımlanmıştır". Bu nedenle, static_assert()C'de hiç mevcut değildir. Ayrıca buraya bakın: en.cppreference.com/w/cpp/language/static_assert - static_assert"(C ++ 11'den beri)" var olduğunu gösterir. Cevabımın güzelliği, sadece C ++ 11 ve sonraki sürümler yerine gcc'nin C90 ve sonraki sürümlerinde ve ayrıca herhangi bir C ++ 11 ve sonraki sürümlerde çalışmasıdır static_assert(). Ayrıca, cevabımla ilgili karmaşık olan nedir? Sadece bir çift #define.
Gabriel Staples

static_assertC11'den beri C'de tanımlanmıştır. Genişleyen bir makrodur _Static_assert. en.cppreference.com/w/c/error/static_assert . Ek olarak ve cevabınızın aksine _Static_assertc99 ve c90'da gcc'de yoktur (sadece gnu99 ve gnu90'da). Bu, standartla uyumludur. Temel olarak, yalnızca gnu90 ve gnu99 ile derlendiğinde fayda sağlayan ve gerçek kullanım alanını önemsiz ölçüde küçük yapan çok fazla ekstra iş yaparsınız.
Hoşçakal SE

> "_Static_assert gcc'de c99 ve c90'da mevcut değildir (sadece gnu99 ve gnu90'da)". Ne demek istediğini anlıyorum. Bu bir gcc uzantısıdır, bu yüzden haklısınız. > "Temelde çok fazla ekstra iş yaparsınız". Katılmıyorum; 2 son derece basit tanım, hiçbir şekilde "çok fazla" ekstra iş değildir. Ne demek istediğini şimdi anlıyorum. Hala yaptığım şeyin yararlı olduğunu ve burada sunulan bilgi ve cevapların tümüne değer kattığını düşünüyorum, bu yüzden olumsuz oyu hak ettiğini düşünmüyorum. Ayrıca, "gcc C90 ve sonrası" veya "g90 ve sonrası" yerine "C90 ve sonrası" demekle ilgili hatam, cevabımda değil, sadece yukarıdaki yorumumdaydı.
Gabriel Staples

Gerçekte yanlış olduğu için olumsuz oy haklı çıktı. Yanlış ifadeleri düzeltirseniz, yanıtı tekrar kontrol ederim ve olumsuz oyumu geri çekebilirim. Gerekmiyorsa yine de böyle bir kod eklemek (yani gnu90 ve gnu99 ile çalışmıyorsanız) netlik açısından yararlı değildir ve daha fazla dağınıklık ekler. Kullanım alanınız varsa buna değer olabilir. Ama gnu99 / 90 ve c ++ 11 uyumluluğunun gerekli olduğu kullanım durumunun nadirliğini merak ediyorum.
Hoşçakal SE

0

Gerçekten basit ve taşınabilir bir şey isteyenler, ancak C ++ 11 özelliklerine erişimi olmayanlar için, tam olarak yazdım. Normal olarak
kullanın STATIC_ASSERT(isterseniz aynı işlevde iki kez yazabilirsiniz) ve GLOBAL_STATIC_ASSERTilk parametre olarak benzersiz bir ifade ile işlevlerin dışında kullanın .


Açıklama:
Öncelikle, varsa kesinlikle kullanmak isteyeceğiniz gerçek iddiaya sahip olup olmadığınızı kontrol eder.
Eğer yapmazsanız, predicatınızı alıp kendi kendine bölerek iddia eder. Bu iki şey yapar.
Sıfır ise, id est, iddia başarısız oldu, sıfıra bölme hatasına neden olur (aritmetik, bir dizi bildirmeye çalıştığı için zorlanır).
Sıfır değilse, dizi boyutunu olarak normalleştirir 1. Dolayısıyla, iddia başarılı olsaydı, yükleminiz -1(geçersiz) olarak değerlendirildiği için başarısız olmasını istemezsiniz veya 232442(optimize edilmiş olsaydı büyük alan israfı, IDK).
İçin STATIC_ASSERTbu parantez içinde sarılır, bu değişken kapsamları bir blok yaparassertyani birçok kez yazabilirsin.
Ayrıca void, unused variableuyarılardan kurtulmanın bilinen bir yolu olan yayınlar .
Çünkü GLOBAL_STATIC_ASSERT, bir kod bloğunun içinde olmak yerine, bir ad alanı oluşturur. Ad alanlarına işlevlerin dışında izin verilir. uniqueBunu bir kereden fazla kullanırsanız, çakışan tanımları durdurmak için bir tanımlayıcı gerekir.


Benim için GCC ve VS'12 C ++ üzerinde çalıştı


2
C'de ad alanı yok
martinkunev

ah, whoops, soruyu yanlış oku. Görünüşe göre buraya C ++ 'a bir cevap aramak için geldim (cevabımın son satırına bakıyorum), bu yüzden başkalarının da aynı şeyi yapması durumunda onu burada bırakacağım
Hashbrown

0

Bu, "kullanılmayanları kaldır" seçenek setiyle çalışır. Global parametreleri kontrol etmek için bir global fonksiyon kullanabilirim.


1
Çalışırsa, bunu yalnızca bir yürütülebilir dosyanın kaynağında yapar.
Kodlayıcı

0

Bu bazı eski gcc için çalıştı. Maalesef hangi sürüm olduğunu unuttum:

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.