Я уверен, что понимаю смысл rvalue
а также lvalue
, Что мне не ясно, так это разыменование rvalue
,
Учти это:
#define GPIO_BASE 0x20200000
#define GPFSEL1 (*(volatile unsigned int *)(AUX_MU_BASE + 0x4))
GPFSEL1 — это разыменование беззнакового указателя int. Указатель int без знака является физическим адресом аппаратного регистра.
Я читал, что это обычная техника в программировании на голом железе для прямого доступа к аппаратным регистрам. Пока что я могу использовать его без проблем.
Теперь я хочу reference
в GPFSEL1
как член структуры. Это возможно?
struct MotorControl
{
u8 pwm_pin;
u8 ctrla_pin;
u8 ctrlb_pin;
volatile unsigned int* MYGPFSEL; // this is not the same thing so does not work
}
Учитывая эту функцию ниже, что является правильным способом ссылки GPFSEL1
который определен в другом месте и как разыменовать его, чтобы установить его значение?
MotorContorl group
group.MYGPFSEL = ?
void set(unsigned char pin_number, bool high)
{
if (high)
{
// how to correct this statement
group.MYGPFSEL |= 1<< pin_number;
}
}
«Разыменование» — это глагол — вы разыменовываете указатель, чтобы получить доступ к значению, на которое он указывает.
Преобразование типов является простым — если у вас есть указатель на целое число, и вы разыменовываете его, теперь вы работаете с целым числом. Это lvalue, так как он (по построению) занимает место в памяти, и его можно (обычно) изменить.
int x = 10;
int* xptr = &x;
*xptr = 5; // *xptr is an lvalue
std::cout << "X: " << x << std::endl; // Prints 5
Однако указатель должен указывать на место в памяти. Если вы только начнете с
int* xptr;
*xptr = 5;
Вы получите ошибку, так как вы пытаетесь разыменовать указатель, который не указывает на действительный адрес памяти. (И если это произойдет, это просто совпадение, и вы неверно измените значение.)
Если вам нужна ссылка на GPFSEL1 в качестве члена вашей структуры MotorControl, вы не сможете инициализировать структуру, не передав ей объект, на который она будет ссылаться. Вместо этого вы, вероятно, захотите указатель внутри структуры, с которым гораздо проще работать:
MotorControl myMotorControl;
myMotorControl.MYGPFSEL = GPFSELF1;
Ваш текущий подход будет работать нормально, вам просто нужно правильно инициализировать переменную-член, например,
struct MotorControl control;
control.MYGPFSEL = (volatile unsigned int *)(AUX_MU_BASE + 0x4);
в качестве альтернативы вы можете инициализировать его как
control = &GPFSEL1;
учитывая, что вы определили GPFSEL1, как в исходном вопросе:
#define GPFSEL1 (*(volatile unsigned int *)(AUX_MU_BASE + 0x4))`
Теперь вы можете прочитать реестр:
foo = *control.MyGPFSEL;
или установите регистр:
*control.MyGPFSEL = 123;
На этот вопрос легко ответить, разыменование может быть левой частью присваивания, поэтому это l-значение.
Простой случай для иллюстрации:
char sz[] {"hello"};
*sz = 'j';
Если что-то может быть левой стороной присваивания, это по определению является l-значением.