Tạo cảnh báo trình biên dịch nếu thiếu dấu phẩy khởi tạo const char *


53

Tôi đang sử dụng các bảng chuỗi ký tự rất nhiều trong mã C của mình. Tất cả các bảng trông ít nhiều giống như thế này:

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

Vấn đề với mã ở trên là nếu bảng dài hơn và bị sửa đổi trong quá trình phát triển, thỉnh thoảng tôi quên dấu phẩy. Mã biên dịch mà không gặp vấn đề với dấu phẩy bị thiếu, nhưng chương trình của tôi cuối cùng bị lỗi khi chuỗi cuối cùng được đặt thành NULL. Tôi đã sử dụng trình biên dịch MinGW và Keil để xác minh.

Có cách nào để tạo cảnh báo trình biên dịch cho khởi tạo của tôi nếu dấu phẩy bị thiếu không?


1
Điều gì xảy ra khi bạn chỉ đơn giản là quên thêm trạng thái vào bảng này?
Jeroen3

1
@ Jeroen3 đúng điều này sẽ gây ra lỗi tương tự. Sử dụng một xác nhận tĩnh kiểm tra độ dài danh sách đối với STATE_AMOUNT cũng giải quyết được vấn đề này.
Jonny Schubert

Câu trả lời:


62

Gói tất cả const char*trong một cặp ngoặc đơn sẽ giải quyết vấn đề như trong đoạn trích sau:

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

Nếu bạn quên dấu phẩy, bạn sẽ gặp lỗi biên dịch tương tự như: error: called object is not a function or function pointer

BẢN THỬ TRỰC TIẾP


Lưu ý rằng nếu bạn quên dấu phẩy thì điều thực sự xảy ra là C sẽ thực sự nối hai chuỗi (hoặc nhiều hơn) cho đến dấu phẩy tiếp theo hoặc kết thúc mảng. Chẳng hạn, giả sử bạn quên dấu phẩy như sau:

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

Đây là những gì gcc-9.2tạo ra (trình biên dịch khác tạo mã tương tự):

.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

Rõ ràng là ba chuỗi cuối cùng được nối và mảng không phải là độ dài bạn mong đợi.


33

Bạn có thể để trình biên dịch đếm mảng và tạo thông báo lỗi nếu kết quả không mong muốn:

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

Xem chủ đề này để biết các ý tưởng để thực hiện _Static_assertnếu trình biên dịch của bạn rất cũ và không hỗ trợ nó.

Là một phần thưởng, điều này cũng có thể giúp ích khi bạn thêm các trạng thái mới nhưng quên cập nhật bảng chuỗi. Nhưng bạn cũng có thể muốn xem xét X Macros.


Chết tiệt .... đây là câu trả lời chính xác tôi vừa gõ!
Thợ hàn

11

Tôi đã luôn sử dụng một tham chiếu đến một mảng có kích thước rõ ràng để giải quyết điều này.

// 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-crooking.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
Một khẳng định tĩnh có vẻ như một giải pháp thanh lịch hơn nhiều. Tôi cho rằng bạn có thói quen làm điều này trước khi các xác nhận tĩnh được thực hiện như một phần của ngôn ngữ? Bây giờ bạn có còn thấy bất kỳ lợi thế nào của điều này so với xác nhận tĩnh xác minh kích thước dự kiến ​​của mảng không?
Cody Grey

2
@CodyGray: Vâng, đây là xác nhận trước khi bạn đề cập đến nó
Vịt Mooing

9

Điều này không mang trình biên dịch vào để giúp bạn, nhưng tôi thấy việc viết nó như dưới đây giúp con người không dễ dàng bỏ dấu phẩy:

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

3
Áp dụng một cái gì đó vào cuối cũng dễ dàng hơn. Bạn không phải chỉnh sửa dòng trước để thêm dấu phẩy. (Lý do chính cho dấu phẩy bị thiếu.)
datafiddler

@datafiddler: Đồng ý. Nó cũng hữu ích để tinh chỉnh danh sách các cột trong lệnh SQL SELECT, khi bạn bắt đầu và không bình luận chúng. Bạn thường muốn thay đổi cái cuối cùng; bạn hiếm khi muốn thay đổi đầu tiên. Bằng cách này, bạn không phải sửa đổi nhiều dòng để nhận xét một mục.
JonathanZ hỗ trợ MonicaC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.