Простите мне, если это сталкивается тривиального вопроса - я обычно - парень систем управления (plc's и автоматизация), но нашел меня вовлеченным в некоторые встроенные проекты микроконтроллера и ПК в последнее время.
Скажем, у меня есть функция, которая принимает указатель на массив 'байтов команды', обычно 5 или 10 байтов в длине, как так:
char cmd_i2c_read(unsigned char *cmd, unsigned short cmd_len) { ... }
Я хочу декодировать байты команды (*cmd).
Это лучше формируется к:
Создайте локальные переменные, указывающие на цель каждого байта:
unsigned char device_address = cmd[2];
unsigned char register_address = cmd[3];
unsigned char num_bytes = cmd[4];
// use the local variables:
if(num_bytes ≤ 0xFF) {
do_stuff(device_address, register_address, num_bytes);
}
Создайте локальные указатели:
unsigned char *device_address = &cmd[2];
unsigned char *register_address = &cmd[3];
unsigned char *num_bytes = &cmd[4];
// use the pointers:
if(*num_bytes ≤ 0xFF) {
do_stuff(*device_address, *register_address, *num_bytes);
}
Индексируйте *cmd массив непосредственно:
if(cmd[4] <= 0xFF) { do_stuff(cmd[2], cmd[3], cmd[4]); }
Вариант 1 понятен, но немного многословен. Вариант 2 мне совсем не нравится, а 3 трудно понять. Лично я предпочитаю использовать структуры для таких вещей.
typedef struct {
unsigned char whatever[2];
unsigned char device_address;
unsigned char register_address;
unsigned char num_bytes;
} CMD;
CMD * pcmd = (CMD *)&cmd[0];
// use the local variables:
if(num_bytes ≤ 0xFF) {
do_stuff(pcmd->device_address, pcmd->register_address, pcmd->num_bytes);
Я предпочитаю пункт 3, но все зависит от предпочтений.
ИМХО, первый способ лучше. Его гораздо легче читать, чем номер 3, потому что вам не нужно знать сигнатуру функции, чтобы понять, какие у нее параметры.
Для больших структур данных я бы выбрал номер 2, т.е. использовал бы указатель, чтобы не копировать значения. Но в данном случае разница не существенна, и я думаю, что */& немного ухудшают читабельность.
Определенно №1. Это делает остальную часть кода более читаемой, а использование указателя вместо обычной переменной усложняет задачу без всякой причины. Компилятор оптимизирует любой микроскопический прирост производительности, который вы можете получить от №3.
1 мне нравится больше всего, он намного читабельнее, чем другие.
Если производительность является важным вопросом (я имею в виду важность, например, «нам нужно каждые 0.01% ускорение, которое мы можем получить "), вам придется провести тест, потому что это действительно зависит от компилятора, какая последовательность завершится в самом быстром коде (1 может содержать ненужные копии, 2 может содержать избыточные нагрузки из-за ограничений наложения указателей, 3 может приведет к чрезмерной загрузке, если распределитель регистров действительно испорчен)
Для меня это зависит от того, что вы будете делать с буфером,
При прочих равных условиях, я бы предпочел, чтобы буфер был структурой (предполагаются искривления выравнивания), а в противном случае, чтобы буфер индексировался напрямую, хотя и не с помощью магических чисел.
Если бы это был я, я бы написал это как №3, но с символическими именами для индексов массива, чтобы улучшить читаемость: -
#define DEVICE_ADDRESS 2
#define REGISTER_ADDRESS 3
#define NUM_BYTES 4
if (cmd[NUM_BYTES] <= 0xFF) {
do_stuff(cmd[DEVICE_ADDRESS], cmd[REGISTER_ADDRESS], cmd[NUM_BYTES]);
}
Вы, конечно, можете замените макросы на const int, enums и т. д. Причина, по которой мне нравится этот вариант, заключается в том, что два других варианта требуют использования дополнительных локальных переменных. Компилятор может или не может оптимизировать их, в зависимости от его реализации и выбранного вами уровня оптимизации, но мне они просто кажутся ненужным уровнем дополнительного косвенного обращения.