Я поместил приведение просто, чтобы показать неодобрение уродливой дыры в системе типов, которая позволяет скомпилировать код, такой как следующий фрагмент, без диагностики, даже если никакие преобразования не используются для плохого преобразования:
double d;
void *p = &d;
int *q = p;
Хотелось бы, чтобы этого не было (и его нет в C ++), и поэтому я разыграл. Это представляет мой вкус и мою политику программирования. Я не только разыгрываю указатель, но и эффективно, голосую, и изгоняю демонов глупости . Если я не могу на самом деле изгнать глупость , тогда, по крайней мере, позвольте мне выразить желание сделать это с жестом протеста.
На самом деле, хорошей практикой является использование malloc
(и друзей) с функциями, которые возвращают unsigned char *
, и в основном никогда не используют void *
в вашем коде. Если вам нужен универсальный указатель на любой объект, используйте char *
или unsigned char *
и используйте приведение в обоих направлениях. Возможно, можно расслабиться только при использовании таких функций, как memset
и memcpy
без приведений.
Что касается приведения типов и совместимости с C ++, если вы пишете свой код так, чтобы он компилировался как на C, так и на C ++ (в этом случае вам необходимо привести приведенное значение malloc
при его назначении к чему-то другому, кроме void *
), вы можете сделать очень полезную вещь для себя: вы можете использовать макросы для приведения, которые преобразуются в приведения в стиле C ++ при компиляции в C ++, но уменьшаются до приведения в C при компиляции в C:
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
Если вы придерживаетесь этих макросов, то простой grep
поиск в вашей кодовой базе по этим идентификаторам покажет вам, где находятся все ваши приведения, поэтому вы можете проверить, являются ли какие-либо из них неправильными.
Затем, если вы будете регулярно компилировать код с C ++, он будет принудительно использовать соответствующее приведение. Например, если вы используете strip_qual
просто для удаления const
или volatile
, но программа изменяется таким образом, что теперь требуется преобразование типов, вы получите диагностику и вам придется использовать комбинация приведений для получения желаемой конверсии.
Чтобы помочь вам придерживаться этих макросов, у компилятора GNU C ++ (не C!) Есть прекрасная функция: дополнительная диагностика, которая производится для всех случаев приведения типов в стиле C.
-Wold-style-cast (C++ and Objective-C++ only)
Warn if an old-style (C-style) cast to a non-void type is used
within a C++ program. The new-style casts (dynamic_cast,
static_cast, reinterpret_cast, and const_cast) are less vulnerable
to unintended effects and much easier to search for.
Если ваш код на C компилируется как C ++, вы можете использовать эту опцию -Wold-style-cast
, чтобы выяснить все вхождения синтаксиса (type)
, которые могут проникнуть в код, и выполнить эту диагностику, заменив его соответствующим выбор из перечисленных выше макросов (или комбинации, если необходимо).
Эта обработка преобразований является единственным крупнейшим техническим обоснованием для работы в «Чистом С»: комбинированный диалект С и С ++, который, в свою очередь, технически оправдывает приведение возвращаемого значения malloc
.