为什么将未签名的字符声明为“ Volatile”会使它与“非易失性”的未签名字符不兼容?

时间:2019-09-16 18:10:02

标签: c pointers volatile

我正在尝试使用一个易失的无符号字符作为函数的指针数组参数。我创建了函数:

static void queue_to_serial(unsigned char *queue)

我正在为函数使用“静态”,因为它仅在当前源文件中使用。然后我调用该函数并提供地址:

queue_to_serial(&queue);

我收到一个错误:

Error[Pe167]: argument of type "unsigned char volatile (*)[8]" is incompatible with parameter of 
type "unsigned char *"

该数组被声明为易失性,但是据我所知,它不应该进行优化,因为它可以异步更改

static volatile unsigned char queue[MAX_NUM_CONNECTED_VALVES];  

该数组也被声明为静态的,因为我想保留分配给该变量的值。除非数组在准确的时间更新,否则函数将不会运行,这在我看来应该可以工作,因为易失性无符号字符使用的内存与无符号字符相同。如何使变量变得可变?还是将其声明为指针会导致它在其他位置占用内存?谢谢

2 个答案:

答案 0 :(得分:3)

编写

之类的函数时
int foo(char* x);

编译器将为此函数生成一些代码,而 对此一无所知,您将投射char*并传递哪种参数对此。因此,它将假设x是指向某些常规内存的指针,该内存可以按常规方式进行优化,例如:

int foo(char* x) 
{
    a = x[0];
    b = x[0];
    return a + b;
}

可以优化为类似return 2 * x[0];的方式-从x[0]执行一次读取。

现在考虑

int bar(volatile char* x) 
{
    a = x[0];
    b = x[0];
    return a + b;
}

这次,编译器必须x生成两次读取。

为什么重要?认为x指向某个硬件计数器寄存器,每次读取该寄存器都会增加。因此,假设其初始值为0,则foo(x)调用将返回0,并且计数器的值为1bar(x)将返回1,并且计数器的值为2

答案 1 :(得分:1)

volatile是在1989年ANSI C中与const同时标准化的,并遵循相同的规则归为与类型限定符相同的类别。

如果您有

void function(char *ptr);

然后出于明显的原因,如果您传入const char对象的地址,则会进行诊断:

{ const char c = 'A'; function(&c); } /* diagnostic required */

从形式上讲,它的工作方式是不能将指向更高限定类型的指针隐式转换为指向更低限定类型的指针。

由于volatileconst属于同一词汇类别(这是一个限定词),因此它的处理方式相同。

此外,如果通过不带有volatile限定符的左值访问定义的对象volatile,则该行为是不确定的。 (请注意,这是一个比const宽松的要求区域:const对象可通过非const-lvalue访问;只有未定义的修改。)我认为这是在语言中添加的,以证明这种处理的合理性。作为类似于const的限定词的易失性概念;这实际上没有多大意义。只是“它的样子”。