Hasilkan peringatan kompiler jika inisialisasi const char * array koma hilang

53

Saya sering menggunakan string literal tables dalam kode C saya. Semua tabel ini terlihat kurang lebih seperti ini:

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

Masalah dengan kode di atas adalah jika tabel menjadi lebih panjang dan dimodifikasi selama pengembangan, saya lupa koma dari waktu ke waktu. Kode dikompilasi tanpa masalah dengan koma yang hilang, tetapi program saya berakhir mogok ketika string terakhir diatur ke NULL. Saya menggunakan kompiler MinGW dan Keil untuk memverifikasi.

Apakah ada cara untuk menghasilkan peringatan kompiler untuk inisialisasi saya jika koma hilang?

Jonny Schubert
sumber
1
Apa yang terjadi ketika Anda lupa menambahkan status ke tabel ini?
Jeroen3
1
@ Jeroen3 benar ini akan menyebabkan kesalahan yang sama. Menggunakan pernyataan statis menguji panjang daftar terhadap STATE_AMOUNT menyelesaikan masalah ini juga.
Jonny Schubert

Jawaban:

62

Membungkus setiap const char*dalam tanda kurung harus menyelesaikan masalah seperti yang ditunjukkan dalam cuplikan berikut:

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

Jika Anda lupa koma, Anda akan mendapatkan kesalahan kompilasi yang mirip dengan: error: called object is not a function or function pointer

LIVE DEMO


Perhatikan bahwa jika Anda lupa koma, apa yang sebenarnya terjadi adalah bahwa C akan benar-benar menyatukan dua (atau lebih) string hingga koma berikutnya, atau akhir array. Misalnya, Anda lupa koma seperti yang ditunjukkan di bawah ini:

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;    
}

Inilah yang gcc-9.2menghasilkan (kompiler lain menghasilkan kode serupa):

.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

Jelas bahwa tiga string terakhir digabungkan dan array bukan panjang yang Anda harapkan.

Davide Spataro
sumber
33

Anda bisa membiarkan kompilator menghitung array dan menghasilkan pesan kesalahan jika hasil yang tidak diharapkan:

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" );

Lihat utas ini untuk gagasan untuk diterapkan_Static_assert jika kompiler Anda sudah sangat tua dan tidak mendukungnya.

Sebagai bonus, ini juga dapat membantu ketika Anda menambahkan status baru tetapi lupa memperbarui tabel string. Tetapi Anda mungkin ingin melihat X Macro juga.

MM
sumber
Sial .... ini adalah jawaban tepat yang akan saya ketik!
The Welder
11

Saya selalu menggunakan referensi ke array berukuran eksplisit untuk menyelesaikan ini.

// 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;
Mooing Duck
sumber
4
Pernyataan statis sepertinya merupakan solusi yang jauh lebih elegan. Saya kira Anda punya kebiasaan melakukan ini sebelum pernyataan statis diterapkan sebagai bagian dari bahasa? Apakah Anda sekarang masih melihat keuntungan dari ini atas pernyataan statis yang memverifikasi ukuran array yang diharapkan?
Cody Gray
2
@CodyGray: Ya, ini adalah pra-statis-menegaskan sekarang bahwa Anda menyebutkannya
Mooing Duck
9

Ini tidak membawa kompiler untuk membantu Anda, tetapi saya merasa menulisnya seperti di bawah ini memudahkan manusia untuk tidak menjatuhkan koma:

static const char* const stateNames[STATE_AMOUNT] =
{
      "Init state"
    , "Run state"
    , "Pause state"
    , "Error state"
};
JonathanZ mendukung MonicaC
sumber
3
Menambahkan sesuatu pada akhirnya juga lebih mudah. Anda tidak perlu mengedit baris sebelumnya untuk menambahkan koma. (Alasan utama untuk koma yang hilang.)
datafiddler
@datafiddler: Setuju. Ini juga berguna untuk memperbaiki daftar kolom dalam perintah SQL SELECT, ketika Anda memberi komentasi dan menghapus komentar. Anda sering ingin mengubah yang terakhir; Anda jarang ingin mengubah yang pertama. Dengan cara ini Anda tidak perlu mengubah beberapa baris untuk berkomentar satu item.
JonathanZ mendukung MonicaC