Const char * dizi başlatma virgülünün eksik olması durumunda derleyici uyarısı oluştur


53

Dize değişmez tabloları C kodumda çok kullanıyorum. Bu tabloların tümü aşağı yukarı şöyle görünür:

static const char* const stateNames[STATE_AMOUNT] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};

Yukarıdaki kod ile ilgili sorun, tablo uzar ve geliştirme sırasında değiştirilir, zaman zaman virgül unutmak. Kod, eksik bir virgülle sorunsuz olarak derlenir, ancak programım son dize olarak ayarlandıkça çöküyor NULL. Doğrulamak için MinGW ve Keil derleyicilerini kullandım.

Virgül eksikse başlatmam için derleyici uyarısı oluşturmanın bir yolu var mı?


1
Bu tabloya bir durum eklemeyi unutursanız ne olur?
Jeroen3

1
@ Jeroen3 doğru bu aynı hataya neden olur. Liste uzunluğunu STATE_AMOUNT ile test eden statik bir assert kullanmak da bu sorunu çözer.
Jonny Schubert

Yanıtlar:


62

const char*Bir çift parantez içinde her birini sarmak , sorunu aşağıdaki snippet'te gösterildiği gibi çözmelidir:

static const char* const stateNames[5] =
{
    ("Init state"),
    ("Run state"),
    ("Pause state")     //comma missing
    ("Pause state3"),
    ("Error state")
};

Bir virgül unutursanız, aşağıdakine benzer bir derleme hatası alırsınız: error: called object is not a function or function pointer

CANLI DEMO


Virgül unutursanız, gerçekte olan şey C'nin iki (veya daha fazla) dizeyi bir sonraki virgül veya dizinin sonuna kadar birleştireceğine dikkat edin. Örneğin, virgülün aşağıda gösterildiği gibi unuttuğunu varsayalım:

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state" //comma missing
    "Pause state3" //comma missing
    "Error state"
};

int main(void)
{  
    printf("%s\n", stateNames[0]);
    return 0;    
}

Bu gcc-9.2üretir (diğer derleyiciler benzer kod üretir):

.LC0:
        .string "Init state"
        .string "Run state"
        .string "Pause statePause state3Error state" ; oooops look what happened
        .quad   .LC0
        .quad   .LC1
        .quad   .LC2
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    puts
        mov     eax, 0
        pop     rbp
        ret

Son üç dizenin birleştirildiği ve dizinin beklediğiniz uzunlukta olmadığı açıktır.


33

Derleyicinin diziyi saymasına ve beklenmeyen bir sonuç çıkarsa bir hata iletisi oluşturmasına izin verebilirsiniz:

enum { STATE_AMOUNT = 4 };

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state"    // <--- missing comma
    "Error state",
};

_Static_assert( sizeof stateNames / sizeof *stateNames == STATE_AMOUNT,
        "oops, missed a comma" );

Uygulanacak fikirler için bu konuya bakın_Static_assertDerleyiciniz çok eskiyse ve desteklemiyorsa .

Bonus olarak, bu, yeni durumlar eklediğinizde de yardımcı olabilir, ancak dize tablosunu güncellemeyi unutur. Ancak X Makrolarına da bakmak isteyebilirsiniz.


Kahretsin .... Bu sadece yazacağım kesin cevaptı!
Kaynakçı

11

Bunu çözmek için her zaman açıkça boyutlandırılmış bir diziye referans kullandım.

// no explicit size here
static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;

http://coliru.stacked-crooked.com/a/593fc2eac80782a6

main.cpp:10:32: error: reference to type 'const char *const [5]' could not bind to an lvalue of type 'const char *const [4]'
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;

4
Statik bir iddia çok daha şık bir çözüm gibi görünüyor. Sanırım bunu, statik iddialar dilin bir parçası olarak uygulanmadan önce yapma alışkanlığınız var mı? Şimdi, dizinin beklenen boyutunu doğrulayan statik bir iddiaya göre bunun herhangi bir avantajını görüyor musunuz?
Cody Gray

2
@CodyGray: Evet, bundan bahsettiğinizde statik öncesi iddia edildi
Mooing Duck

9

Bu, derleyiciyi size yardımcı olmak için getirmez, ancak aşağıdaki gibi yazmanın insanların virgül atmamasını kolaylaştırdığını düşünüyorum:

static const char* const stateNames[STATE_AMOUNT] =
{
      "Init state"
    , "Run state"
    , "Pause state"
    , "Error state"
};

3
Sonunda bir şey eklemek de daha kolaydır. Virgül eklemek için önceki satırı düzenlemeniz gerekmez. (Eksik virgülün ana nedeni.)
datafiddler

@datafiddler: Kabul etti. Ayrıca, bir SQL SELECT komutundaki sütun listesine ince ayar yapmak ve yorum kaldırmak için kullanışlıdır. Sık sık sonuncuyu değiştirmek istersiniz; ilkini nadiren değiştirmek istersiniz. Bu şekilde, bir öğeyi yorumlamak için birden fazla satırı değiştirmeniz gerekmez.
JonathanZ,
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.