unsigned short A=B[0]+B[1]*255;
Сводится по сути к B[0]+B[1]<<8; Т.е. основное - две операции: сложение+сдвиг. И с умножением ошиблись - нужно умножать на 0x100, т.е. 256. Если переменная A сильно нужна - добавить пересылки в память. По факту - скорее всего переменная A оптимизируется и значение возьмется из регистра процессора.
unsigned short A; CopyMemory (&A,B,2);
Вызов ф-ции. Итого - организация стека (push/pop, настройка регистров, передача параметров), код самой ф-ции. Если функция встраиваемая - уже лучше, но по скорости все равно будет хуже, чем сложение+сдвиг, т.к. имеется работа с памятью. Оптимизировать не удастся.
uint16_t A = *(uint16_t*)&B[0];
и клоны. Всего лишь две пересылки (память -> регистр, регистр -> память). В лучшем случае соптимизируется и значение A далее опять же возьмется из регистра. Т.е. по факту - одно чтение из памяти. И никаких записей в память.
Вообще на самом деле надо брать и смотреть ассемблерный листинг. Сейчас все компиляторы оптимизирующие. И просто грубо преобразовывать однозначно одну инструкцию языка в одну или несколько инструкций процессора не будут. А будут подгадывать максимально эффективный вариант по одному из критериев. Их на самом деле два: скорость и размер. И для каждого процессора правила оптимизации свои.
Касательно double:
double A = *(double*)&B[0];
Но с даблом я бы поостерегся. Дело в том, что целые числа хранятся как целые, поразрядно. Каждый байт последовательно друг за другом. И запись
unsigned short A=B[0]+B[1]*256;
unsigned long C=B[0]+B[1]<<8+B[2]<<16+B[3]<<24;
работает. А внутреннее представление double существенно сложнее. Мантисса, экспонента, знаки... Фу. бр. И просто выдрать из double определенные разряды сложнее.