2013-01-31 41 views
2

我試圖找到一種方法來使枚舉「無符號」。有沒有辦法使C90標準(符合MISRA-C 2004)無符號枚舉

enum{ 
    x1 = 0, 
    x2, 
    x3 
}; 
uint8_t = x2; /* <--- PC-LINT MISRA-C 2004 will complain about mixing signed and unsigned here */ 

當然,我可以添加一個類型轉換擺脫錯誤的,這是耗時且容易出錯。

uint8_t = (uint8_t)x2; /* This works, but is a lot of extra work over the course of 1000s of lines of code*/ 

那麼,有沒有辦法讓一個特定的枚舉unsigned MISRA-C 2004會喜歡?

+1

這是來自PC-LINT的一個愚蠢的警告。 'x2'的值是靜態已知的,並且已知適合'uint8_t'的範圍,所以類型是無關緊要的。同樣愚蠢的是發出'char x = 1LL;'的警告。 –

+0

@R ..問題在於MISRA與PC-Lint無關。 'uint8_t x = 1;'不符合MISRA-C,它不需要在已簽名和未簽名類型之間進行隱式轉換。 – ouah

+2

這是MISRA關於枚舉的一個問題:枚舉常量和枚舉變量不一定具有相同的類型,因此它們不一定是兼容的。這是C語言中許多奇怪的不一致之處,也不是MISRA的錯,它們只是試圖避開語言的所有這些非理性缺陷。 – Lundin

回答

9

沒有標準的C方式來控制enum所選的類型。您可以通過添加一個值,以強制類型是無符號枚舉做到在實施的具體方式有時,如:

enum { 
    x1, 
    x2, 
    x3, 
    giant_one_for_forcing_unsigned = 0x80000000; 
}; 

但是這還沒有標準C,或者(因爲所提供的價值將不適合在int)。不幸的是,你幾乎不走運。下面是從標準的相關位:

6.7.2.2枚舉符,第4款

每個枚舉類型應char,有符號整數類型,或者一個無符號整數類型兼容。類型的選擇是實現定義的,但應能夠表示枚舉的所有成員的值。枚舉類型是不完整的,直到緊接在終止枚舉器聲明列表之後並在其後完成。

你可能會更好使用#define而不是enum,使您的常量:

#define x1 0U 
#define x2 1U 
#define x3 2U 

uint8_t x = x2; 
+0

使用0x80000000不一定會強制枚舉爲無符號,因爲這是一個有符號的32位整數的有效表示。 –

+0

卡爾 - 我寧願認爲情況就是如此。謝謝。 – Tom

+2

@BobMurphy:0x80000000不能表示爲一個帶符號的32位整數。它可以被簽名的唯一方法是如果int大於32位。 –

5

不僅是有沒有在C90的方式來指定一個enum採取對無符號類型,但在C90:

聲明爲枚舉常數的標識符的類型爲int

這也適用於C99(6.4.4.3)。如果你想要一個無符號類型,你正在尋找一個語言擴展。

枚舉類型可能比其他0​​東西,但常數本身必須具有int類型。

6

有一些關注這裏,那裏是轉換錯誤輕微的潛力,這MISRA正試圖使你避免:

  • 枚舉常數,即x1等在你的榜樣,是保證是類型int(1)。但枚舉變量和變量類型枚舉不能保證是相同的類型(2),如果你不幸它被定義爲一個小整數類型,從而服從整數提升規則。

  • MISRA禁止將大整數類型的隱式轉換轉換爲較小的轉換類型,主要是爲了避免無意中截斷值,同時也避開各種隱式升級規則。

您的具體MISRA遵從性錯誤實際上來自上述後者,違反規則10.3(3)。

您可以通過向「基礎類型」(預期類型)添加明確轉換來解決此問題,在這種情況下,轉換爲uint8_t。或者你可以通過永遠不使用枚舉來解決它,用#defines替換它們。這聽起來可能非常激進,但請記住,C沒有任何類型安全性,因此使用枚舉或許可讀可讀性沒有明顯的好處。

這是有點共同以這種方式,以取代枚舉:

#define FALSE 0 
#define TRUE 1 
typedef uint8_t BOOL; 

(儘管在這個例子中,目的主要是使BOOL類型的便攜式,具有保證是8位,從未16位,在可能的情況下發生的,這是一個枚舉)


參考文獻:

(1)C11 6.2.7.7/2:

「定義枚舉常量 的值的表達式應該是一個整型常量表達式,其值可表示爲 作爲int。」

(2)C11 6.2.7.7/4:

「每一枚舉類型應與炭,有符號整數 類型,或一個無符號整數類型兼容類型的選擇是 實現定義的,但是應該能夠表示枚舉的所有成員的 值。「

(3)MISRA-C:2004規則10.3:

「整數類型的複雜表達式的值可僅被轉換爲 一個類型,它是窄相同的符號性的和作爲底層 表達式的類型「。

0

您可以通過包含足夠大的值以使其無法放入int(每個規範)來強制它被無符號簽名。這對於> = sizeof int類型非常簡單,但unsigned char/short更加複雜,並且需要編譯器特定的打包。當然,實現技術上仍然可以將UINT_MAX表示爲一個無符號長整數......但這並不是我見過的。

#include <stdio.h> //only included for printf example 
#include <limits.h> 
#include <stdint.h> 

/** set up some helper macros **/ 
#ifdef _MSC_VER 
    #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) 
#else /* for gcc, clang, icc and others */ 
    #define PACK(...) __VA_ARGS__ __attribute__((__packed__)) 
#endif 
#define _PASTE(x,y) x ## y 
#define PASTE(x,y) _PASTE(x,y) 

/* __LINE__ added for semi-unique names */ 
#define U_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(U_DUMMY , __LINE__) = UINT_MAX } 
#define UL_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(UL_DUMMY , __LINE__) = ULONG_MAX } 
#define SZ_ENUM(n, ...) /* useful for array indices */ \ 
    enum n { __VA_ARGS__ , PASTE(SZ_DUMMY , __LINE__) = SIZE_MAX } 
#define ULL_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(ULL_DUMMY , __LINE__) = ULLONG_MAX } 
#define UC_ENUM(n,...) \ 
    PACK(enum n { __VA_ARGS__ , PASTE(UC_DUMMY , __LINE__) = UCHAR_MAX }) 
#define US_ENUM(n,...) \ 
    PACK(enum n { __VA_ARGS__ , PASTE(US_DUMMY , __LINE__) = USHRT_MAX }) 

下面是一個檢查,看看它按預期工作:

typedef UC_ENUM(,a) A_t; 
typedef US_ENUM(,b) B_t; 
typedef U_ENUM(,c) C_t; 
typedef UL_ENUM(,d) D_t; 
typedef ULL_ENUM(,e) E_t; 
typedef SZ_ENUM(,e) F_t; 
int main(void) { 
    printf("UC %d,\nUS %d,\nU %d,\nUL %d,\nULL %d,\nSZ %d,\n",sizeof(A_t), 
    sizeof(B_t),sizeof(C_t),sizeof(D_t),sizeof(E_t),sizeof(F_t)); 
    return 0; 
} 

更像一個標準的枚舉聲明這比簡單的版本我用,這需要一個額外的命名有少許不同參數最後枚舉,而不是__LINE__黑客(這也是返回-1錯誤功能非常有用,因爲它會轉換爲U * _MAX) 下面是該版本的外觀:

#define U_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = UINT_MAX } 
#define UL_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = ULONG_MAX } 
#define ULL_ENUM(n,err, ...)  enum n { __VA_ARGS__ , err = ULLONG_MAX} 
#define SZ_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = SIZE_MAX } 
#define UC_ENUM(n, err, ...) PACK(enum n { __VA_ARGS__ , err = UCHAR_MAX }) 
#define US_ENUM(n, err, ...) PACK(enum n { __VA_ARGS__ , err = USHRT_MAX }) 

除了在char中包含枚舉或緊湊性之外,size_t枚舉最有趣,因爲它們可以用作數組索引而無需額外的MOV指令。

typedef SZ_ENUM(message_t,MSG_LAST,MSG_HELLO,MSG_GOODBYE,MSG_BAD) message_t; 
static const char *messages[]={"hello","goodbye","bad message"}; 
void printmsg(message_t msg){ 
    if (msg > MSG_BAD) msg = MSG_BAD; 
    (void) puts(messages[msg]); 
} 

注意:如果使用C++ 11 VS C,可以enum Foo : char { A, B, C};enum class Bar : size_t { X, Y, Z};

0

在addtion到@Carl's answer,得到一些的enum聲明的好處,並導致一些未簽名的類型,代碼可以使用下面的。

// Form values 0, 5, 6 
enum { 
x1, 
x2 = 5, 
x3 
}; 

// Form values 0u, 5u, 6u 
#define ux1 (1u * x1) 
#define ux2 (1u * x2) 
#define ux3 (1u * x3) 

這可能無助於int範圍之外的枚舉常量。

當然代碼可以做OP轉換,而不是OP知道。

// uint8_t = x2; 
uint8_t = x2 * 1u; 
相關問題