Есть ли у замены неявного преобразования явным приведением какие-либо побочные эффекты?
Лучше явно приводить, чем просто использовать неявное преобразование?
Например, у меня есть перечисление...
/*This enum represents the various encryption types for wifi. For wifi capable devices, a bitwise & result of all supported encryption types should be returned.*/
typedef enum wifi_encryptionType {
/*Unknown encryption - default value, and for if wifi standard is ever expanded.*/
WIFIENCTYPE_UNKNOWN = 0,
/*No encryption - an open network.*/
WIFIENCTYPE_NONE = 1,
/*WEP encryption - all widths.*/
WIFIENCTYPE_WEP = 2,
/*WPA 1 with a preshared key using Temporal Key Integrity Protocol.*/
WIFIENCTYPE_WPA_PSK_TKIP = 4,
/*WPA 1 with a preshared key using Advanced Encryption Standard via CCMP. */
WIFIENCTYPE_WPA_PSK_AES = 8,
/*WPA 2 with a preshared key using Temporal Key Integrity Protocol.*/
WIFIENCTYPE_WPA2_PSK_TKIP = 16,
/*WPA 2 with a preshared key using Advanced Encryption Standard via CCMP.*/
WIFIENCTYPE_WPA2_PSK_AES = 32
} wifi_encryptionType;
Который я использую в структуре.
typedef struct {
char ssid[32];
wifi_encryptionType encryption;
wifi_mode mode;
} WifiNetwork;
и я использую значение поля этой структуры в качестве параметра для вызова функции...
read_uint8(readBuffer, &network.encryption);
//read_uint8 takes a struct pointer containing some buffer info, and a uint8_t pointer.
Я получаю предупреждение.
warning: passing argument 2 of 'read_uint8' from incompatible pointer type
expected 'uint8_t *' but argument is of type 'enum wifi_encryptionType *'
Я понимаю, что означает предупреждение. "Имейте в виду, что чтение uint8_t
и положить его в wifi_encryptionType
В поле можно поместить значения, которые не соответствуют ни одному из объявленных вами значений. "
Преобразование типов выполняется прямо сейчас.
Лучше сделать это явным приведением? Есть ли какие-либо преимущества в явном приведении актеров к игре или какие-либо недостатки?
2 ответа
Предупреждение в этом случае не просто придирки к компилятору. Это может потенциально сломаться.
Причина в том, что enum
тип может иметь другой размер, чем uint8_t
, Стандарт C11 гарантирует только это (раздел 6.7.2.2)
Каждый перечисляемый тип должен быть совместим с
char
, целочисленный тип со знаком или целочисленный тип без знака.
Если вам не повезло, и enum
имеет такое же представление, как, например, int
тогда вы будете эффективно передавать указатель на начальный байт int
, В системе с прямым порядком байтов этот байт не будет иметь такое же значение, как int
, даже если значение будет соответствовать.
Еще одна проблема (которая, вероятно, здесь бы не применялась) - это строгие псевдонимы, что означает, что компилятору разрешено предполагать, что одни и те же данные не доступны как два разных типа. Примером будет иметь int*
и float*
указать на то же место, записав в это место через int*
, а затем читать из него через float*
, Строгие правила псевдонимов позволяют оптимизировать код, так как компилятор может предположить, что запись через int*
не испортит значение float*
указывает на (чтобы его пришлось перезагружать, например, в регистр).
Причина, по которой строгое алиасинг, вероятно, здесь не применим uint8_t
на практике почти наверняка unsigned char
, для которого компилятор делает исключение. char*
(или же unsigned char*
) может использоваться для доступа к памяти любого типа объекта. Если бы это не было разрешено, то не было бы никакого безопасного способа выполнить "необработанные" байтовые операции, например, например, memcpy()
,
enum
(большинство Ликли) является int
,
Таким образом, вы передаете адрес int
где адрес uint8_t
ожидается.
Компилятор говорит вам это:
warning: passing argument 2 of 'read_uint8' from incompatible pointer type expected 'uint8_t *' but argument is of type 'enum wifi_encryptionType *'
Это путь к катастрофе.
Чтобы обойти это, используйте временную промежуточную переменную:
{
uint8_t tmp = network.encryption;
read_uint8(readBuffer, &tmp);
network.encryption = tmp;
}