volatile结构体成员赋值:C99标准下是否存在风险?
关于PC-Lint Plus 931警告的C99标准合规性疑问
#include <stdint.h> typedef struct { uint32_t a; } foo_t; volatile foo_t foo; volatile uint32_t b; int main(void) { foo.a = b; return 0; }
这段代码触发了PC-Lint Plus的931诊断信息:
Note 931: both sides have side effects
当表达式的左右两侧都存在副作用时触发此提示。示例:n++ + f(),这种情况通常无害。像n++ + n这类真正有问题的情况会通过警告564捕获。
此诊断支持CERT C EXP10-C规则——不要依赖子表达式的求值顺序或副作用的发生顺序。
支持MISRA C 2012规则1.3
支持MISRA C 2004规则1.2
我清楚副作用和序列点的概念,但无法理解上述赋值操作为何会触发这个警告。我认为这里存在两个副作用:写入foo.a、读取b,但它们针对的是不同对象。根据C语言求值顺序规则:
- 若对某个标量对象的副作用与对同一标量对象的另一副作用无顺序关系,行为未定义。
- 若对某个标量对象的副作用与使用同一标量对象值的计算无顺序关系,行为未定义。
显然这段代码不存在未定义行为。另外我发现,如果b是非volatile变量,或者仅将结构体成员而非整个结构体声明为volatile,都不会触发该警告。
请问从C99标准的角度来看,该赋值操作是否真的存在风险?
从C99标准的角度来说,这段代码不存在未定义行为,也没有合规性风险。
原因如下:
- 赋值操作的左右两侧分别涉及两个独立的volatile对象:
foo.a(属于volatile结构体foo)和b。根据C99标准,对不同对象的副作用即使无顺序关系,也不会导致未定义行为——只有当同一对象的副作用或值计算无顺序约束时才会触发未定义行为。 - 读取volatile变量本身属于副作用(因为volatile对象的读取可能关联外部硬件状态),写入
foo.a也是副作用,但两者针对不同对象,完全符合标准要求。
PC-Lint Plus触发931警告,本质是工具的保守性检查:它只是检测到表达式两侧都存在副作用,而没有进一步区分是否是同一对象。尤其是当整个结构体被声明为volatile时,工具可能将对结构体成员的访问等同于对整个结构体对象的操作,进而触发了针对“两侧均有副作用”的提示,但这并不代表代码存在标准层面的风险。
内容的提问来源于stack exchange,提问作者radoslav006
相关产品推荐
相关产品推荐

