Khẳng định tĩnh trong C


86

Cách tốt nhất để đạt được thời gian biên dịch xác nhận tĩnh trong C (không phải C ++), đặc biệt nhấn mạnh vào GCC là gì?

Câu trả lời:


90

Tiêu chuẩn C11 thêm _Static_asserttừ khóa.

Điều này được triển khai kể từ gcc-4.6 :

_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */

Vị trí đầu tiên cần phải là một biểu thức hằng tích phân. Khe thứ hai là một ký tự chuỗi không đổi có thể dài ( _Static_assert(0, L"assertion of doom!")).

Tôi cần lưu ý rằng điều này cũng được thực hiện trong các phiên bản gần đây của clang.


4
[... dường như được thực hiện bởi gcc, bởi clang ...] Bạn có thể khẳng định hơn rằng ;-) _Static_assertlà một phần của tiêu chuẩn C11 và bất kỳ trình biên dịch nào hỗ trợ C11, sẽ có nó.
PP

1
Có thể sử dụng điều này ở phạm vi tệp (bên ngoài bất kỳ chức năng nào) không? Bởi vì tôi nhận được error: expected declaration specifiers or '...' before 'sizeof'cho dòng static_assert( sizeof(int) == sizeof(long int), "Error!); (Tôi đang sử dụng C không phải C ++ bằng cách)
user10607

@ user10607 Tôi rất ngạc nhiên vì điều này không hoạt động .. Chờ đã, bạn đang thiếu một trích dẫn ở cuối chuỗi lỗi của mình. Đặt nó vào và lấy lại. Điều này hoạt động đối với tôi trên gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");Trên macine của tôi, tôi gặp lỗi.
emsr

Tôi có gcc 4.8.2 trên Ubuntu. Trích dẫn bị thiếu là lỗi đánh máy nhận xét (tôi đã có nó trong mã). Đây là dòng đầu tiên trong tệp sau một vài tiêu đề bao gồm. Trình biên dịch cung cấp cho tôi hai lỗi chính xác giống nhau: error: expected declaration specifiers or '...' before 'sizeof'AND error: expected declaration specifiers or '...' before string constant(anh ấy đang tham chiếu đến "Error!"chuỗi) (cũng như: Tôi đang biên dịch với -std = c11. Khi đặt khai báo bên trong một hàm, tất cả đều hoạt động tốt (không thành công và thành công như mong đợi))
user10607

2
@ user10607 Tôi cũng phải chỉ định -std = gnu11 trên dòng lệnh. Tôi thực sự ngạc nhiên khi có sự khác biệt giữa 4,8 và 4,8. Tôi có một nguồn chỉ với một dòng. Tôi cũng đã sử dụng tiêu chuẩn C _Static_assertkhông phải C ++ ish static_assert. Bạn cần `#include <khẳng định.h> để nhận macro static_assert.
emsr

92

Điều này hoạt động trong phạm vi chức năng và phi chức năng (nhưng không hoạt động bên trong các cấu trúc, liên kết).

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

STATIC_ASSERT(1,this_should_be_true); 

int main()
{
 STATIC_ASSERT(1,this_should_be_true); 
}
  1. Nếu xác nhận thời gian biên dịch không thể khớp, thì một thông báo gần như dễ hiểu sẽ được tạo bởi GCC sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. Macro có thể hoặc nên được thay đổi để tạo ra một tên duy nhất cho typedef (nghĩa là nối __LINE__ở cuối static_assert_...tên)

  3. Thay vì một bậc ba, điều này cũng có thể được sử dụng #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1], nó sẽ hoạt động ngay cả trên trình biên dịch cc65 cũ đã gỉ (cho cpu 6502).

CẬP NHẬT: Vì lợi ích hoàn chỉnh, đây là phiên bản với__LINE__

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
// token pasting madness:
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
#define COMPILE_TIME_ASSERT(X)    COMPILE_TIME_ASSERT2(X,__LINE__)

COMPILE_TIME_ASSERT(sizeof(long)==8); 
int main()
{
    COMPILE_TIME_ASSERT(sizeof(int)==4); 
}

UPDATE2: Mã cụ thể của GCC

GCC 4.3 (tôi đoán) đã giới thiệu các thuộc tính chức năng "lỗi" và "cảnh báo". Nếu một lệnh gọi đến một hàm có thuộc tính đó không thể bị loại bỏ thông qua loại bỏ mã chết (hoặc các biện pháp khác) thì một lỗi hoặc cảnh báo sẽ được tạo ra. Điều này có thể được sử dụng để xác nhận thời gian biên dịch với các mô tả lỗi do người dùng xác định. Nó vẫn còn để xác định cách chúng có thể được sử dụng trong phạm vi không gian tên mà không cần dùng đến một hàm giả:

#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })

// never to be called.    
static void my_constraints()
{
CTC(sizeof(long)==8); 
CTC(sizeof(int)==4); 
}

int main()
{
}

Và đây là cách nó trông như thế này:

$ gcc-mp-4.5 -m32 sas.c 
sas.c: In function 'myc':
sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true

1
Trong Visual Studio nó chỉ nói "subscript Negative", không nhắc đến tên biến ...
SZX

Nordic Mainframe - tùy chọn 3 trong câu trả lời của bạn không hoạt động trên tiếng kêu.
Elazar

1
Về giải pháp cuối cùng (GCC 4.3 + -specific): Giải pháp này rất mạnh mẽ, vì nó có thể kiểm tra bất kỳ thứ gì mà trình tối ưu hóa có thể tìm ra, nhưng nó sẽ thất bại nếu tối ưu hóa không được bật. -OgTuy nhiên, mức tối ưu hóa tối thiểu ( ) thường có thể đủ để tính năng này hoạt động và không ảnh hưởng đến việc gỡ lỗi. Người ta có thể xem xét việc làm cho xác nhận tĩnh trở thành xác nhận không hoạt động hoặc thời gian chạy nếu __OPTIMIZE__(và __GNUC__) không được xác định.
Søren Løvborg

Trong đoạn mã có phiên bản LINE (CẬP NHẬT: Vì lợi ích hoàn chỉnh, đây là phiên bản có `LINE), khi biên dịch, nó bị lỗi ở dòng (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), bạn có thể sửa bằng cách thêm một đoạn mã khác cấp như bên dưới: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Xác nhận tại: ## L "");
Sundar

Tôi sử dụng một cái gì đó tương tự như __LINE__phiên bản trong gcc 4.1.1 ... đôi khi hơi khó chịu khi hai tiêu đề khác nhau có một tiêu đề trên cùng một dòng được đánh số!
MM

10

cl

Tôi biết câu hỏi đề cập rõ ràng đến gcc, nhưng chỉ để hoàn thiện ở đây là một tinh chỉnh cho các trình biên dịch của Microsoft.

Sử dụng typedef mảng có kích thước âm không thuyết phục cl tìm ra một lỗi phù hợp. Nó chỉ nói error C2118: negative subscript. Trường bit có chiều rộng bằng không có giá tốt hơn về mặt này. Vì điều này liên quan đến việc gõ một cấu trúc, chúng ta thực sự cần sử dụng các tên kiểu duy nhất. __LINE__không cắt ngang - có thể có một COMPILE_TIME_ASSERT()dòng trên cùng một dòng trong tiêu đề và tệp nguồn, và biên dịch của bạn sẽ bị hỏng. __COUNTER__đến để giải cứu (và nó đã có trong gcc kể từ 4.3).

#define CTASTR2(pre,post) pre ## post
#define CTASTR(pre,post) CTASTR2(pre,post)
#define STATIC_ASSERT(cond,msg) \
    typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
        CTASTR(static_assertion_failed_,__COUNTER__)

Hiện nay

STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)

dưới clcho:

error C2149: 'static_assertion_failed_use_aosystem_compiler_luke': trường bit được đặt tên không được có chiều rộng bằng 0

Gcc cũng đưa ra một thông điệp dễ hiểu:

error: zero width cho trường bit 'static_assertion_failed_use_aosystem_compiler_luke'


4

Từ Wikipedia :

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );

15
Sẽ tốt hơn nếu bạn liên kết với nguồn thực: jaggersoft.com/pubs/CVu11_3.html
Matt Joiner

Nó không hoạt động trong gcc 4.6 - nó nói "nhãn trường hợp không giảm xuống một hằng số nguyên". Nó có một điểm.
Liosan

chắc cả hai bạn đều đã chuyển waaay vào lúc này, nhưng cuối cùng tôi đã viết bài của riêng mình (xem câu trả lời của tôi ). Tôi đã từng @MattJoiner liên kết của bạn để hỗ trợ tôi
Hashbrown

Và nếu bạn có thể thấy phiền, hãy cho tôi biết nếu nó phù hợp với bạn, @Liosan. Tôi đã chỉ mới bắt đầu đào sâu vào C ++ vì vậy tôi đã đến muộn để đảng
Hashbrown

Đối với Visual C ++, nó được tích hợp static_assert kể từ phiên bản 2010 và nó hoạt động ở cả chế độ c ++ và c. Tuy nhiên, nó không được tích hợp c99 _Static_assert.
ddbug

3

Tôi KHÔNG khuyên bạn nên sử dụng giải pháp bằng cách sử dụng typedef:

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

Khai báo mảng với typedeftừ khóa KHÔNG đảm bảo được đánh giá tại thời điểm biên dịch. Ví dụ: mã sau trong phạm vi khối sẽ biên dịch:

int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);

Tôi muốn giới thiệu điều này thay thế (trên C99):

#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]

statictừ khóa, mảng sẽ được xác định tại thời điểm biên dịch. Lưu ý rằng xác nhận này sẽ chỉ hoạt động với CONDnhững gì được đánh giá tại thời điểm biên dịch. Nó sẽ không hoạt động với (tức là quá trình biên dịch sẽ thất bại) với các điều kiện dựa trên các giá trị trong bộ nhớ, chẳng hạn như các giá trị được gán cho các biến.


4
Trong khi điều này có hiệu quả, nó cũng sẽ làm tăng yêu cầu bộ nhớ của bạn.
sherrellbc

1
error: 'static_assertion_INVALID_CHAR_SIZE' được xác định nhưng không được sử dụng [-Werror = used-variable]
Alex

2

Nếu sử dụng macro STATIC_ASSERT () với __LINE__, có thể tránh xung đột số dòng giữa một mục nhập trong tệp .c và một mục nhập khác trong tệp tiêu đề bằng cách bao gồm __INCLUDE_LEVEL__.

Ví dụ :

/* Trickery to create a unique variable name */
#define BOOST_JOIN( X, Y )      BOOST_DO_JOIN( X, Y )
#define BOOST_DO_JOIN( X, Y )   BOOST_DO_JOIN2( X, Y )
#define BOOST_DO_JOIN2( X, Y )  X##Y
#define STATIC_ASSERT(x)        typedef char \
        BOOST_JOIN( BOOST_JOIN(level_,__INCLUDE_LEVEL__), \
                    BOOST_JOIN(_assert_on_line_,__LINE__) ) [(x) ? 1 : -1]

1

Cách cổ điển là sử dụng một mảng:

char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];

Nó hoạt động bởi vì nếu xác nhận là đúng, mảng có kích thước 1 và nó hợp lệ, nhưng nếu nó sai, kích thước -1 sẽ gây ra lỗi biên dịch.

Hầu hết các trình biên dịch sẽ hiển thị tên của biến và trỏ đến phần bên phải của mã nơi bạn có thể để lại nhận xét cuối cùng về xác nhận.


Việc kết hợp điều này thành một #define STATIC_ASSERT()macro loại chung và cung cấp thêm các ví dụ chung và đầu ra trình biên dịch mẫu từ các ví dụ chung của bạn bằng cách sử dụng STATIC_ASSERT()sẽ cung cấp cho bạn nhiều lượt ủng hộ hơn và tôi nghĩ rằng kỹ thuật này có ý nghĩa hơn.
Gabriel Staples

Tôi không đồng ý. Trình biên dịch nhìn thấy các macro suy nghĩ và đưa ra một thông báo khó hiểu hơn.
Paolo.Bolzoni

1

Từ Perl, cụ thể là perl.hdòng 3455 (đã <assert.h>bao gồm trước):

/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
   time invariants. That is, their argument must be a constant expression that
   can be verified by the compiler. This expression can contain anything that's
   known to the compiler, e.g. #define constants, enums, or sizeof (...). If
   the expression evaluates to 0, compilation fails.
   Because they generate no runtime code (i.e.  their use is "free"), they're
   always active, even under non-DEBUGGING builds.
   STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
   file scope (outside of any function).
   STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
   function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
   builtin in C++11.  But IBM XL C V11 does not support _Static_assert, no
   matter what <assert.h> says.
*/
#  define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
   'typedef char x[n]' where n is not a compile-time constant.
   We want to enforce constantness.
*/
#  define STATIC_ASSERT_2(COND, SUFFIX) \
    typedef struct { \
        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
#  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
#  define STATIC_ASSERT_DECL(COND)    STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
   error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND)      STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END

Nếu static_assertcó sẵn (từ <assert.h>), nó được sử dụng. Ngược lại, nếu điều kiện sai, một trường bit có kích thước âm sẽ được khai báo, điều này làm cho quá trình biên dịch không thành công.

STMT_START/ STMT_ENDlà các macro mở rộng thành do/ while (0), tương ứng.


1

Bởi vì:

  1. _Static_assert() hiện được định nghĩa bằng gcc cho tất cả các phiên bản của C và
  2. static_assert() được định nghĩa trong C ++ 11 trở lên

STATIC_ASSERT()Do đó, macro đơn giản sau đây hoạt động trong:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) trở lên
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (không chỉ định std)

Xác định STATIC_ASSERTnhư sau:

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")

Bây giờ sử dụng nó:

STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed" 

Ví dụ:

Đã kiểm tra trong Ubuntu bằng gcc 4.8.4:

Ví dụ 1:gcc đầu ra tốt (nghĩa là: STATIC_ASSERT()mã hoạt động, nhưng điều kiện sai, gây ra xác nhận thời gian biên dịch):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: Trong hàm 'main'
static_assert.c: 78: 38: error: static khẳng định không thành công: "(1> 2) không thành công"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") không thành công")
^
static_assert.c: 88: 5: lưu ý: trong phần mở rộng của macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Ví dụ 2:g++ -std=c++11 đầu ra tốt (nghĩa là: STATIC_ASSERT()mã hoạt động, nhưng điều kiện là sai, gây ra xác nhận thời gian biên dịch):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: Trong hàm 'int main ()'
static_assert.c: 74: 32: lỗi: xác nhận tĩnh không thành công: (1> 2) không thành công
#define _Static_assert static_assert / * static_assertlà một phần của C ++ 11 trở lên * /
^
static_assert.c: 78: 38: lưu ý: khi mở rộng macro '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") không thành công")
^
static_assert.c: 88: 5: lưu ý: trong phần mở rộng của macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Ví dụ 3: đầu ra C ++ không thành công (nghĩa là: mã khẳng định không hoạt động bình thường, vì điều này đang sử dụng phiên bản C ++ trước C ++ 11):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: warning: mã định danh 'static_assert' là một từ khóa trong C ++ 11 [-Wc ++ 0x-compat]
STATIC_ASSERT (1> 2 );
^
static_assert.c: Trong hàm 'int main ()'
static_assert.c: 78: 99: error: 'static_assert' không được khai báo trong phạm vi này
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) không thành công ")
^
static_assert.c: 88: 5: lưu ý: khi mở rộng macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Toàn bộ kết quả kiểm tra tại đây:

/*
static_assert.c
- test static asserts in C and C++ using gcc compiler

Gabriel Staples
4 Mar. 2019 

To be posted in:
1. /programming/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. /programming/3385515/static-assert-in-c/7287341#7287341

To compile & run:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert

-------------
TEST RESULTS:
-------------

1. `_Static_assert(false, "1. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  NO

2. `static_assert(false, "2. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             NO
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    NO
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

3. `STATIC_ASSERT(1 > 2);` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

*/

#include <stdio.h>
#include <stdbool.h>

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")


int main(void)
{
    printf("Hello World\n");

    /*_Static_assert(false, "1. that was false");*/
    /*static_assert(false, "2. that was false");*/

    STATIC_ASSERT(1 > 2);

    return 0;
}

Có liên quan:

  1. Sử dụng static_assert để kiểm tra các loại được chuyển đến macro [câu trả lời của riêng tôi]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Sử dụng static_assert để kiểm tra các loại được chuyển đến macro
  3. Cách sử dụng xác nhận tĩnh trong C để kiểm tra các loại tham số được truyền cho macro

1
Tại sao quá phức tạp, khi có một static_assertmacro trong assert.h?
Tạm biệt SE

@KamiKaze, tôi rất ngạc nhiên với câu hỏi của bạn, vì có vẻ như bạn chưa thực sự đọc câu trả lời của tôi? Dòng thứ 2 của câu trả lời của tôi nói lên tất cả: "static_assert () được định nghĩa trong C ++ 11 trở lên". Do đó, hoàn toàn static_assert()không có sẵn trong C. Xem thêm tại đây: en.cppreference.com/w/cpp/language/static_assert --it cho thấy static_asserttồn tại "(kể từ C ++ 11)". Cái hay của câu trả lời của tôi là nó hoạt động trong C90 của gcc trở lên, cũng như bất kỳ C ++ 11 nào trở lên, thay vì chỉ trong C ++ 11 trở lên, như static_assert(). Ngoài ra, câu trả lời của tôi có gì phức tạp? Nó chỉ có một vài #defines.
Gabriel Staples

static_assertđược định nghĩa trong C kể từ C11. Nó là một macro mở rộng đến _Static_assert. vi.cppreference.com/w/c/error/static_assert . Ngoài ra và tương phản với câu trả lời của bạn _Static_assertkhông có trong c99 và c90 trong gcc (chỉ trong gnu99 và gnu90). Điều này là phù hợp với tiêu chuẩn. Về cơ bản, bạn thực hiện rất nhiều công việc bổ sung, điều đó chỉ mang lại lợi ích nếu được biên dịch với gnu90 và gnu99 và điều này làm cho usecase thực tế nhỏ đi không đáng kể.
Tạm biệt SE

> "_Static_assert không khả dụng trong c99 và c90 trong gcc (chỉ trong gnu99 và gnu90)". Tôi hiểu ý bạn là gì. Nó là một phần mở rộng gcc, vì vậy bạn đã chính xác. > "Về cơ bản bạn làm rất nhiều việc thêm". Tôi không đồng ý; 2 định nghĩa cực kỳ đơn giản không có nghĩa là "rất nhiều" công việc phụ trội. Nói như vậy, tôi hiểu ý bạn bây giờ. Tôi vẫn nghĩ những gì tôi đã làm là hữu ích và tăng thêm giá trị cho khối kiến ​​thức và câu trả lời được trình bày ở đây, vì vậy tôi không nghĩ điều đó xứng đáng với sự phản đối. Ngoài ra, lỗi của tôi khi nói "C90 trở lên" thay vì "gcc C90 trở lên", hoặc "g90 trở lên", chỉ nằm trong nhận xét của tôi ở trên, không phải trong câu trả lời của tôi.
Gabriel Staples

Vì nó thực sự sai, một phản đối là chính đáng. Nếu bạn sửa các câu sai, tôi sẽ kiểm tra lại câu trả lời và có thể rút lại phiếu phản đối của mình. Vẫn thêm mã như vậy nếu không cần thiết (vì vậy nếu bạn không làm việc với gnu90 và gnu99) không có lợi cho sự rõ ràng và thêm nhiều lộn xộn hơn. Nếu bạn có usecase, nó có thể đáng giá. Nhưng tôi thắc mắc về sự hiếm hoi của usecase nơi yêu cầu khả năng tương thích gnu99 / 90 và c ++ 11.
Tạm biệt SE

0

Đối với những người bạn muốn một cái gì đó thực sự cơ bản và di động nhưng không có quyền truy cập vào các tính năng của C ++ 11, tôi đã viết điều này.
Sử dụng STATIC_ASSERTbình thường (bạn có thể viết nó hai lần trong cùng một hàm nếu bạn muốn) và sử dụng GLOBAL_STATIC_ASSERTbên ngoài các hàm với một cụm từ duy nhất làm tham số đầu tiên.

#if defined(static_assert)
#   define STATIC_ASSERT static_assert
#   define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c)
#else
#   define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;}
#   define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];}
#endif

GLOBAL_STATIC_ASSERT(first, 1, "Hi");
GLOBAL_STATIC_ASSERT(second, 1, "Hi");

int main(int c, char** v) {
    (void)c; (void)v;
    STATIC_ASSERT(1 > 0, "yo");
    STATIC_ASSERT(1 > 0, "yo");
//    STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one
    return 0;
}

Giải thích:
Đầu tiên nó kiểm tra xem bạn có xác nhận thực sự hay không, mà bạn chắc chắn sẽ muốn sử dụng nếu nó có sẵn.
Nếu bạn không, nó xác nhận bằng cách lấy predicate của bạn và chia nó cho chính nó. Điều này làm được hai điều.
Nếu nó là 0, id est, thì xác nhận không thành công, nó sẽ gây ra lỗi chia cho 0 (số học bị ép buộc vì nó đang cố gắng khai báo một mảng).
Nếu nó không phải là 0, nó sẽ bình thường hóa kích thước mảng thành 1. Vì vậy, nếu xác nhận được thông qua, bạn sẽ không muốn nó bị lỗi vì vị từ của bạn được đánh giá là -1(không hợp lệ) hoặc là 232442(lãng phí dung lượng lớn, IDK nếu nó sẽ được tối ưu hóa).
STATIC_ASSERTnó được bao bọc trong dấu ngoặc nhọn, điều này làm cho nó trở thành một khối, phạm vi biếnassert, nghĩa là bạn có thể viết nó nhiều lần.
Nó cũng chuyển nó đến void, đây là một cách đã biết để loại bỏ các unused variablecảnh báo.
Cho GLOBAL_STATIC_ASSERT, thay vì trong một khối mã, nó tạo ra một không gian tên. Không gian tên được phép bên ngoài các chức năng. Cần có số uniquenhận dạng để ngăn chặn mọi định nghĩa xung đột nếu bạn sử dụng định nghĩa này nhiều lần.


Đã làm việc cho tôi trên GCC và VS'12 C ++


2
Không có không gian tên trong C.
martinkunev

à, rất tiếc, đọc sai câu hỏi. Trông giống như tôi đến đây tìm kiếm một câu trả lời cho C ++ anyway (nhìn vào dòng cuối cùng của câu trả lời của tôi), vì vậy tôi sẽ để nó ở đây trong trường hợp người khác làm như vậy
Hashbrown

0

Điều này hoạt động, với tùy chọn "loại bỏ không sử dụng" được đặt. Tôi có thể sử dụng một hàm toàn cục để kiểm tra các tham số toàn cục.

//
#ifndef __sassert_h__
#define __sassert_h__

#define _cat(x, y) x##y

#define _sassert(exp, ln) \
extern void _cat(ASSERT_WARNING_, ln)(void); \
if(!(exp)) \
{ \
    _cat(ASSERT_WARNING_, ln)(); \
}

#define sassert(exp) _sassert(exp, __LINE__)

#endif //__sassert_h__

//-----------------------------------------
static bool tab_req_set_relay(char *p_packet)
{
    sassert(TXB_TX_PKT_SIZE < 3000000);
    sassert(TXB_TX_PKT_SIZE >= 3000000);
    ...
}

//-----------------------------------------
Building target: ntank_app.elf
Invoking: Cross ARM C Linker
arm-none-eabi-gcc ...
../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637'
collect2: error: ld returned 1 exit status
make: *** [ntank_app.elf] Error 1
//

1
Nếu nó hoạt động, nó sẽ chỉ làm như vậy trong nguồn của tệp thực thi.
Coder

0

Điều này đã làm việc cho một số gcc cũ. Xin lỗi vì tôi quên đó là phiên bản nào:

#define _cat(x, y) x##y

#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]

#define sassert(exp) _sassert((exp), __LINE__)

//
sassert(1 == 2);

//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem
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.