Я вставил приведение просто, чтобы показать неодобрение уродливой дыры в системе типов, которая позволяет скомпилировать код, такой как следующий фрагмент, без диагностики, даже если никакие преобразования не используются для плохого преобразования:
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 ++ и Objective-C ++)
Предупреждать, если используется приведение старого типа (в стиле C) к не пустому типу
в программе на C ++. Кастинги нового стиля (dynamic_cast,
static_cast, reinterpret_cast и const_cast) менее уязвимы
непреднамеренные эффекты и гораздо легче искать.
Если ваш код на C компилируется как C ++, вы можете использовать эту -Wold-style-cast
опцию, чтобы выяснить все вхождения (type)
синтаксиса приведения, которые могут проникнуть в код, и проследить за этими диагностиками, заменив его подходящим выбором из перечисленных выше макросов (или комбинация, если необходимо).
Такая обработка преобразований является единственным крупнейшим техническим обоснованием для работы в «Чистом С»: комбинированный диалект С и С ++, который, в свою очередь, технически оправдывает приведение возвращаемого значения malloc
.