2013-12-10 4 views
-1

Допустим, что ваш ужасный босс дает вам следующее задание:Как изменить тип переменной

Написать функцию, которая возвращает второй байт в long. Ниже приведены требования:

  • Используйте следующие функции подписи: uint8_t second_byte(uint32_t l);

Немного вертел решение, которое приходит на ум:

uint8_t second_byte(uint32_t l) 
{ 
    uint8_t b = (l >> 8) & 0xFF; 
    return b; 
} 

Boss возвращается с другим требованием

  • Не используйте бит-скручивание!

Вы покупаете Boss Voodoo Doll и продолжить:

uint8_t second_byte(uint32_t l) 
{ 
    uint8_t b = ((uint8_t*) &l)[1]; 
    return b; 
} 

Boss чувствует себя властная, руки вниз еще одно требование:

  • Не Де-/ссылка !!

Идите и купите руководство по изготовлению бомбы и липкую ленту. После нескольких царапин на голове вы придумали следующую схему:

typedef union { 
    uint8_t u8[4]; 
    uint16_t u16[2]; 
    uint32_t u32; 
} long_u; 

uint8_t second_byte(long_u l) 
{ 
    uint8_t b = long_u.u8[1]; 
    return b; 
} 

Чистота и красота. Вы идете к своему боссу и представляете решение.

Boss упоминает первое требование (функция подпись). Вы спорите со своим начальником об этом глупом требовании, бесплодно: вы босс босса передал это требование.

Может ли кто-нибудь подумать о способе «изменить» тип переменной. что-то вроде этого (но код, который компилируется):

uint8_t second_byte(uint32_t l) 
{ 
    uint8_t b = ((long_u) l).u8[1]; 
    return b; 
} 

Под «изменения» я имею в виду писать код, который убеждает компилятор обработать переменную типа T как тип T'без создания дополнительных инструкций (как с битами и de-/refrencing)

Оптимизация компилятора ПРИМЕЧАНИЕ. Некоторые компиляторы (например, GCC) могут оптимизировать вышеуказанные попытки кодирования, не добавляя никаких инструкций. некоторые не могут (например, IAR). Я ищу конструкцию языка C, которая не зависит от оптимизации компилятора.

Endianness ПРИМЕЧАНИЕ. Я не хочу сосредотачиваться на проблемах энтианности. просто представьте себе uint32_t является 4-байтовой структурой

Проще говоря, у меня есть переменная типа T, связанная с областью в памяти.Я хочу иметь другую переменную типа T', связанных с той же самой области без возможных команд-стоимости (де-/refrencing)

+0

Вы понимаете, что «де-реферирование» код должен фактически генерировать _zero_ дополнительные инструкции, если у вас есть приличный компилятор, не так ли? (Попробуйте взглянуть на сгенерированный код сборки, чтобы подтвердить это.) – DaoWen

+0

Весь этот код зависит от конечности машины и ** второго байта в длительном ** термине просто плохо и неоднозначно. – Artur

+3

Ваш босс ужасен, давая вам такой без учета энтузиазма. Думаю, это неважно, так как все в этом вопросе в любом случае бессмысленно в реальном мире. – Lundin

ответ

0

Используйте указатель union для типаж:

uint8_t second_byte(uint32_t l) 
{ 
    uint32_t *lp = &l; 
    uint8_t b = ((long_u*)lp)->u8[1]; 
    return b; 
} 

это компилируется на моей системе без предупреждений (с -Wall опция также)

ПРИМЕЧАНИЕ: Правильность кода может зависеть от конечности машины.

+0

приводит к дополнительным инструкциям (на моем компиляторе IAR) –

1

Вместо «изменения», просто «копия»:

uint8_t second_byte(uint32_t l) 
{ 
    uint8_t temp[2] ; 
    memcpy(temp, &l, sizeof(temp)) ; 
    return temp[1] ; 
} 

Мой следующий шаг в чудовищность будет рассматривать сборку.

+2

Следует отметить, что это даст разные результаты для систем с различным порядком байтов. –

+0

Но на всех системах он получит «второй» байт. – woolstar

+0

Правда, перефразировал мой комментарий. –

0

Простой литая отлично работает для меня с GCC:

uint8_t second_byte(uint32_t l) 
{ 
    return ((union { uint8_t u8[4]; uint32_t u32; })l).u8[1]; 
} 

Декларирование промежуточную переменную, вероятно, будет более удобным для чтения (и избежать возможных предупреждений компилятора о гипсе):

uint8_t second_byte(uint32_t l) 
{ 
    union { uint8_t u8[4]; uint32_t u32; } data; 
    data.u32 = l; 
    return data.u8[1]; 
} 

Эти два версия функции производит идентичный машинный код при компиляции с gcc с оптимизацией не менее -O1.

На самом деле, я вижу код точно таких же машин для каждой версию этой функции в вашем вопросе и в моем ответе. Это включает версию с битовой перестановкой. Большинство современных компиляторов очень умный. Беспокойство по оптимизации бит-twiddling обычно бывает бессмысленным, потому что компилятор сделает это лучше вас, и вы только жертвуете удобочитаемостью, чтобы получить ту же производительность.

+1

Следует отметить что это даст другой результат для систем с различным порядком байтов. –

+1

@JoachimPileborg - Спецификация была «Напишите функцию, которая возвращает ** второй байт ** длинной». Если бы он возвращал второй-младший байт, тогда порядок байтов может быть проблемой. Опять же, спецификация может быть ошибочной, и она должна быть независимой от байта. – DaoWen

+0

Я использую IAR и получаю 'cast to type 'union Ошибка« не допускается ». sucks –

0

Мы можем использовать это

typedef union { 
     uint8_t u8[4]; 
     uint16_t u16[2]; 
     uint32_t u32; 
} long_u; 

uint8_t second_byte(uint32_t l) 
{ 
     long_u u; 
     memcpy(&u,&l,sizeof(l)); 
     return u.u8[1]; 
} 
+0

добавляет инструкции и вызов функции –

Смежные вопросы