C ++ numaralandırmaları imzalı mı yoksa imzasız mı?


107

C ++ numaralandırmaları imzalı mı yoksa imzasız mı? Ve uzantı olarak, bir girişi <= maksimum değeriniz olduğunu ve dışarıda bırakarak> = minimum değeriniz olduğunu kontrol ederek doğrulamak güvenli midir (0'dan başladığınızı ve 1 artırdığınızı varsayarsak)?


İşaretini gerektiren bir bağlamda bir enum türü kullandığımızda, aslında numaralandırmayı örtük olarak bir integral türe dönüştürmekten bahsediyoruz. C ++ 03 standardı, bunun İntegral Promosyon tarafından yapıldığını söyler, numaralamanın temelindeki türle ilgili hiçbir şey yoktur. Öyleyse, buradaki her yanıtın neden standart tarafından tanımlanmadığından bahsedildiğini anlamıyorum? Beklenen davranışı burada açıkladım: stackoverflow.com/questions/24802322/…
JavaMan

Yanıtlar:


60

Herhangi bir temsile güvenmemelisiniz. Aşağıdaki bağlantıyı okuyun . Ayrıca standart, int veya işaretsiz int'e uymayan bazı değerler olmadıkça int'ten daha büyük olmaması dışında, bir enum için temel tür olarak hangi integral türünün kullanıldığını uygulama tanımlı olduğunu söyler.

Kısacası: imzalı veya imzasız bir numaralandırmaya güvenemezsiniz.


28
Michael Burr'un cevabı (standardını tırnak) aslında ima edebilir eğer olumsuzluğa olarak bir enum değeri tanımlarsanız nedeniyle "numaralandırma tanımlanan tüm numaralayıcı değerleri temsil" edememek tipine imzalanarak buna güveniyor.
Samuel Harmer

101

Kaynağa gidelim. C ++ 03 standardı (ISO / IEC 14882: 2003) belgesi 7.2-5'te (Numaralandırma bildirimleri) şöyle diyor:

Bir numaralandırmanın temeldeki türü, numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilen bir integral türdür. Bir numaralandırma için temel alınan tür olarak hangi integral türünün kullanıldığı uygulama tanımlıdır, tek fark, bir numaralandırıcının değeri bir int veya işaretsiz int'e sığmadığı sürece, temel alınan tür int'den büyük olmamalıdır.

Kısacası, derleyiciniz seçebilir (tabii ki, numaralandırma değerlerinden bazıları için negatif sayılara sahipseniz, imzalanacaktır).


Tüm numaralandırma değerleri küçük, pozitif tamsayılar olduğunda, derleyici tahminlerinden nasıl kaçınabilir ve ona temelde işaretsiz bir tip kullanmasını söyleyebiliriz? (Bir UBsan bulgusunu yakalıyoruz çünkü derleyici bir int seçiyor ve int'in taşması yaşanıyor. Değer işaretsiz ve pozitiftir ve bizim kullanımımız bir azalma veya "negatif adım" sağlamak için işaretsiz sarmaya bağlıdır).
jww

@jww - bu, tam olarak hangi derleyiciyi kullandığınıza bağlı olacak, sanırım. Standart, temeldeki türü belirlemediğinden ve bunu uygulamaya bıraktığından, aracınızın belgelerine bakmanız ve bu seçeneğin mümkün olup olmadığını görmeniz gerekir. Kodunuzda belirli bir davranışı garanti etmek istiyorsanız, ifadede kullandığınız enum üyesini neden atmıyorsunuz?
ysap

22

Onların imzalı veya imzasız olmasına güvenmemelisiniz. Bunları açıkça imzalı veya imzasız yapmak istiyorsanız, aşağıdakileri kullanabilirsiniz:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

11
Yalnızca gelecekteki C ++ 0x standardında.
dalle

3
@dalle Microsoft derleyicisi ayrıca yazılı numaralandırmalara izin verir msdn.microsoft.com/en-us/library/2dzy4k6e(v=vs.80).aspx
teodozjan

15

İmzalı ya da imzasız olduğuna güvenmemelisiniz. Standarda göre, bir enum için temel tür olarak hangi integral türünün kullanıldığı uygulama tanımlıdır. Ancak çoğu uygulamada işaretli bir tamsayıdır.

C ++ 0x'de , aşağıdaki gibi bir numaralandırmanın türünü belirtmenize olanak tanıyan kesin olarak yazılmış numaralandırmalar eklenecektir:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Şimdi bile, enum'u aşağıdaki gibi bir değişken veya parametre türü olarak kullanarak bazı basit doğrulamalar gerçekleştirilebilir:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.

Sanırım ikinci örneğiniz biraz karışık :)
Miral

5

Derleyici numaralandırmaların imzalı veya imzasız olup olmadığına karar verebilir.

Numaralandırmaları doğrulamanın başka bir yöntemi, numaralandırmanın kendisini bir değişken türü olarak kullanmaktır. Örneğin:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.

5

Bazı eski cevaplar bile 44 olumlu oy aldı, hepsine katılmıyorum. Kısacası, sıralamayı önemsememiz gerektiğini düşünmüyorum underlying type.

Öncelikle, C ++ 03 Enum türü, işaret kavramı olmayan, kendine özgü bir türdür. C ++ 03 standardından beridcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Bu nedenle, bir enum türünün işaretinden bahsederken, örneğin <operatörü kullanarak 2 enum işlenenini karşılaştırırken , aslında enum türünü örtük olarak bir integral türüne dönüştürmekten bahsediyoruz. Önemli olan bu integral tipinin işaretidir . Ve numaralandırmayı integral türüne dönüştürürken bu ifade geçerlidir:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

Ve görünüşe göre, numaralamanın altında yatan tipin İntegral Teşvikle hiçbir ilgisi yok. Standart, İntegral Promosyonu şu şekilde tanımladığından:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

Bu nedenle, bir numaralandırma türünün haline gelip gelmediği signed intveya numaralandırmanın temelini oluşturan türü değil, tanımlanan numaralandırıcıların tüm değerlerini içerip içermeyeceğine unsigned intbağlıdır signed int.

İlgili soruma bakın İntegral Türüne Dönüştürüldükten Sonra C ++ Enum Türü İşareti Yanlış


İle derlerken önemlidir -Wsign-conversion. Kodumuzdaki istenmeyen hataları yakalamaya yardımcı olmak için kullanıyoruz. Ancak , standardı belirtmek ve bir numaralamanın kendisiyle ilişkili bir türüne ( karşı ) sahip olmadığına işaret etmek için +1 . signedunsigned
jww


4

Başkalarının imzalı / imzasızlar hakkında daha önce söylediklerine ek olarak, standardın numaralandırılmış bir tür aralığı hakkında söyledikleri şunlardır:

7.2 (6): "e (min) 'in en küçük numaralandırıcı ve e (max)' ın en büyük olduğu bir numaralandırma için, numaralandırmanın değerleri, temel tipin b (min) ila b (max) aralığındaki değerleridir. ), burada b (min) ve b (max), sırasıyla, e (min) ve e (max) depolayabilen en küçük bit alanının en küçük ve en büyük değerleridir. Tanımlanmamış değerlere sahip bir numaralandırma tanımlamak mümkündür numaralandırıcılarından herhangi biri tarafından. "

Yani mesela:

enum { A = 1, B = 4};

e (min) 1 ve e (max) 4 olan numaralandırılmış bir türü tanımlar. Temel alınan tür int olarak işaretlenmişse, gerekli olan en küçük bit alanı 4 bit'e sahiptir ve uygulamanızdaki girişler ikinin tamamlayıcısı ise geçerli aralık enum -8'den 7'ye kadardır. Altta yatan tür işaretsiz ise, 3 biti vardır ve aralık 0'dan 7'ye kadardır. Önemsiyorsanız derleyici belgelerinizi kontrol edin (örneğin, numaralandırıcılar dışında integral değerleri atamak istiyorsanız, numaralandırılmış tür varsa, değerin numaralandırma aralığında olup olmadığını bilmeniz gerekir - sonuçta ortaya çıkan enum değeri belirtilmemişse).

Bu değerlerin işleviniz için geçerli girdi olup olmadığı, numaralandırılmış türde geçerli değerler olup olmadıklarından farklı bir sorun olabilir. Kontrol kodunuz muhtemelen ikincisinden çok birincisi hakkında endişelidir ve bu nedenle bu örnekte en azından> = A ve <= B'yi kontrol etmelisiniz.


0

std::is_signed<std::underlying_type+ Kapsamlı numaralandırmalarla kontrol edinint

https://en.cppreference.com/w/cpp/language/enum şunu ifade eder:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHub yukarı akış .

Derleyin ve çalıştırın:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Çıktı:

0

Ubuntu 16.04, GCC 6.4.0'da test edilmiştir.

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.