Подтвердить что ты не робот

Как безопасно конвертировать/копировать изменчивую переменную?

volatile char* sevensegment_char_value;

void ss_load_char(volatile char *digits) {
    ...
    int l=strlen(digits);
    ...
 }

 ss_load_char(sevensegment_char_value);

В приведенном выше примере я получил предупреждение от компилятора avr-gcc

Warning 6   passing argument 1 of 'strlen' discards 'volatile' qualifier from pointer target type [enabled by default]

Итак, мне нужно как-то скопировать значение из volatile в non-volatile var? Какое безопасное обходное решение?

4b9b3361

Ответ 1

Нет такой вещи, как "встроенный" метод обхода в C. Volatile сообщает компилятору, что содержимое переменной (или в вашем случае память, на которую указывает переменная) может измениться без компилятора, замечающего его и заставляет компилятор считывать данные непосредственно с шины данных, а не использовать, возможно, существующую копию в регистрах. Поэтому ключевое слово volatile используется для предотвращения нечетного поведения, вызванного оптимизацией компилятора. (Я могу объяснить это дальше, если хотите)

В вашем случае у вас есть буфер символов, объявленный как изменчивый. Если ваша программа изменяет содержимое этого буфера в другом контексте, таком как ISR, например, вам необходимо реализовать механизм синхронизации (например, отключить конкретное прерывание или так), чтобы избежать несогласованности данных. После активации "блокировки" (отключение прерывания) вы можете скопировать байт данных байтом в локальный (энергонезависимый) буфер и работать с этим буфером для остальной части подпрограммы.

Если буфер не изменится "вне" контекста ваших запросов на чтение, я предлагаю исключить ключевое слово volatile, поскольку для него нет необходимости.

Чтобы судить о правильном решении, потребуется немного больше информации о вашем конкретном случае использования.

Ответ 2

Стандартные библиотечные процедуры не предназначены для работы с объектами volatile. Простейшим решением является считывание энергозависимой памяти в обычную память перед ее работой:

void ss_load_char(volatile char *digits) {
  char buf[BUFSIZE];
  int i = 0;
  for (i = 0; i < BUFSIZE; ++i) {
    buf[i] = digits[i];
  }
  int l=strlen(buf);
  ...
}

Здесь BUFSIZE - размер области энергозависимой памяти.

В зависимости от того, как настроена энергозависимая память, могут быть процедуры, которые вы должны вызывать, чтобы скопировать содержимое, а не просто использовать цикл. Обратите внимание, что memcpy не будет работать, поскольку он не предназначен для работы с памятью volatile.

Ответ 3

Предупреждение о компиляторе означает, что strlen() не будет обрабатывать ваш указатель как изменчивый, то есть он может кэшировать указатель в регистре при вычислении длины строки. Я думаю, это нормально с тобой.

В общем случае volatile означает, что компилятор не будет кэшировать переменную. Посмотрите на этот пример:

extern int flag;
while (flag) { /* loop*/ }

Это будет цикл навсегда, если flag != 0, поскольку компилятор предполагает, что флаг не изменяется "извне", как другой поток. Если вы хотите подождать на входе какого-либо другого потока, вы должны написать это:

extern volatile int flag;
while (flag) { /* loop*/ }

Теперь компилятор будет действительно смотреть на флаг каждый раз, когда петля цикла. Это может быть больше, чем мы предполагали в этом примере.

В ответ на ваш вопрос: если вы знаете, что делаете, просто выбросьте volatile с помощью int l=strlen((char*)digits).