Нужна помощь по этому синтаксису: #define LEDs (char *) 0x0003010

Я занимаюсь программированием софткорного процессора Nios II от Altera, ниже приведен код в одном из туториалов, мне удалось заставить код работать, протестировав его на оборудовании (плата DE2), однако я не смог понять код.

#define Switches (volatile char *) 0x0003000
#define LEDs (char *) 0x0003010
void main()
{ while (1)
*LEDs = *Switches;
}

Что я знаю о #define, так это то, что он используется для определения константы или макроса, но

  1. почему в приведенном выше коде есть приведение типа (char *) 0x0003010 в #define?
  2. почему две константы, Switches и LEDs, действуют как переменная, а не как константа?

person I'm a frog dragon    schedule 10.06.2010    source источник


Ответы (4)


1) почему в приведенном выше коде есть приведение типа (char *) 0x0003010 в #define?

Макросы препроцессора являются текстовыми заменами. Таким образом, код выходит как

while (1) {
  *(char *) 0x0003010 = *(volatile char *) 0x0003000
}

который повторяется, присваивает содержимое входа (переключателя), отображаемого по адресу 0x3000, выходу (светодиод), отображаемому по адресу 0x3010.

2) почему 2 константы, переключатели и светодиоды действуют как переменная, а не как константа?

Обратите внимание, что это указатель. Таким образом, они всегда указывают на одно и то же место (которое оказывается парой выводов ввода-вывода с отображением памяти или чем-то подобным), но нет гарантии, что содержимое этих постоянных местоположений будет постоянным, и * перед каждым символом препроцессора появляется оператор разыменования указателя.

person dmckee --- ex-moderator kitten    schedule 10.06.2010

Похоже, что Switches и LEDs представляют отображение памяти на фактический ввод (в случае Switches) и вывод (в случае LEDs).

Таким образом, ваши ответы включают в себя:

  1. Байт для входных переключателей отображается в память по адресу 0x0003000. Чтобы получить к нему доступ как к байту, вам нужно сообщить компилятору, что все, что находится по адресу 0x0003000, является char (на самом деле вы сообщаете ему, что значение по этому адресу равно volatile char, чтобы компилятор не оптимизировал тот факт, что значение по этому адресу может измениться в любое время).

  2. Они являются константами, но они являются постоянными указателями. Другими словами, адрес является постоянным, но значения, содержащиеся в этих адресах, непостоянны.

Что происходит, так это то, что каждый такт (или около того) все, что читается из памяти по адресу 0x0003000, затем записывается по адресу 0x0003010. Это создает иллюзию того, что переключатели мгновенно переключают светодиоды.

person Mark Rushakoff    schedule 10.06.2010

В C макросы представляют собой простые замены.

Каждый раз, когда компилятор видит LEDs в вашем коде, он заменяет его на (char *) 0x0003010.

Таким образом, ваш код фактически такой же, как этот:

void main()
{
    while (1)
        *(char *) 0x0003010 = *(volatile char *) 0x0003000;
}
person Artelius    schedule 10.06.2010
comment
Препроцессор, а не компилятор. - person Dig; 10.06.2010
comment
@dig: В C этапы предварительной обработки являются частью процесса компиляции, и правильно сказать, что компилятор выполняет эти задачи, хотя в некоторых реализациях может использоваться отдельная программа для выполнения этапов предварительной обработки. - person James McNellis; 10.06.2010
comment
@James: но в этом контексте понятнее сказать препроцессор, где вы имеете в виду препроцессор. - person dmckee --- ex-moderator kitten; 10.06.2010
comment
@james: это один и тот же процесс, в котором работают оба модуля. но препроцессор и компилятор не одно и то же. препроцессор запускается до компилятора и закладывает основу для выполнения компилятором своей работы. сказать, что препроцессор и компилятор — это одно и то же, все равно что сказать, что компоновщик и компилятор — это одно и то же. - person Dig; 10.06.2010
comment
предварительная обработка является частью первого шага компилятора (для многих компиляторов и в некоторых реализациях она может быть полностью встроена, хотя, вероятно, это происходит нечасто); ваше утверждение похоже на то, что вы должны разделить компилятор на все шаги, которые он выполняет как концептуально разделенные действия (поэтому вы должны добавить, по крайней мере, также шаги сборки и компоновки); почему, говоря о компиляции, я могу предназначать C для сборки (если этот шаг выполнен, концептуально всегда выполняется...), сборки для машинных кодов, упаковки машинных кодов в формат объекта, связывания obj..., а не первого preproc шаг? - person ShinTakezou; 10.06.2010
comment
часть должна быть частью... не все компиляторы всех языков встраивают препроцессор или вызывают внешний... - person ShinTakezou; 10.06.2010

Без приведения типов в #define они не будут рассматриваться как char* и volatile char*. То, что находится в *Switches, копируется в *LED.

person vpit3833    schedule 10.06.2010