如何安全地转换/复制volatile变量?

Pab*_*blo 12 c volatile

volatile char* sevensegment_char_value;

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

 ss_load_char(sevensegment_char_value);
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我收到了avr-gcc编译器的警告

Warning 6   passing argument 1 of 'strlen' discards 'volatile' qualifier from pointer target type [enabled by default]
Run Code Online (Sandbox Code Playgroud)

所以我必须以某种方式将值从volatile复制到非易失性var?什么是安全的解决方法?

jun*_*nix 9

C中没有"内置"变通方法这样的东西.易失性告诉编译器,变量的内容(或者在你的情况下是变量指向的内存)可以改变,而编译器没有注意到它并强制编译器直接从数据总线读取数据,而不是使用寄存器中可能存在的副本.因此,volatile关键字用于避免通过编译器优化引起的奇怪行为.(如果你愿意,我可以进一步解释)

在您的情况下,您有一个声明为volatile的字符缓冲区.如果您的程序在不同的上下文中更改此缓冲区的内容(例如ISR),则必须实现某种同步机制(如禁用特定中断等)以避免数据不一致.在获取"锁定"(禁用中断)之后,您可以逐字节地将数据复制到本地(非易失性)缓冲区,并在此缓冲区上处理剩余的例程.

如果缓冲区不会在读取访问的上下文"外部"发生变化,我建议省略volatile关键字,因为它没有用处.

要判断正确的解决方案,需要更多关于您的确切用例的信息.


cxx*_*xxl 5

编译器警告仅意味着 strlen() 不会将您的指针视为易失性,即在计算字符串长度时,它可能会将指针缓存在寄存器中。我想,这对你没问题。

一般来说,volatile意味着编译器不会缓存变量。看这个例子:

extern int flag;
while (flag) { /* loop*/ }
Run Code Online (Sandbox Code Playgroud)

如果flag != 0,这将永远循环,因为编译器假定标志不会“从外部”更改,就像不同的线程一样。如果你想等待其他线程的输入,你必须这样写:

extern volatile int flag;
while (flag) { /* loop*/ }
Run Code Online (Sandbox Code Playgroud)

现在,每次循环循环时,编译器都会真正查看标志。这可能更符合我们在此示例中的意图。

回答您的问题:如果您知道自己在做什么,只需使用int l=strlen((char*)digits).


eca*_*mur 5

标准库例程并非设计用于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);
  ...
}
Run Code Online (Sandbox Code Playgroud)

BUFSIZE是易失性存储器的区域大小。

根据易失性存储器的配置方式,可能应该调用一些例程来复制内容,而不仅仅是使用循环。请注意,这memcpy将无法使用,因为它并非设计用于volatile内存。