C++ 通过易失性引用/指针访问声明的非易失性对象是否会在所述访问上授予易失性规则?
这将是一个很长的过程,为了将其上下文化并提供尽可能多的信息,我必须遍历各种链接和引用——这通常是我们进入C/C++标准兔子洞的唯一方法。如果你对这篇文章有更好的引用或任何其他改进,请让我知道。但要提前总结,目的是从两个命题中找到真理:C++ 通过易失性引用/指针访问声明的非易失性对象是否会在所述访问上授予易失性规则?,c++,c,pointers,language-lawyer,volatile,C++,C,Pointers,Language Lawyer,Volatile,这将是一个很长的过程,为了将其上下文化并提供尽可能多的信息,我必须遍历各种链接和引用——这通常是我们进入C/C++标准兔子洞的唯一方法。如果你对这篇文章有更好的引用或任何其他改进,请让我知道。但要提前总结,目的是从两个命题中找到真理: < C++ >标准(通过导入;参阅注释),C++标准要求通过代码> >易失性*>代码>或易失性>代码>必须引用一个最初声明为“代码>易失性的对象,以便具有易失性< /代码>语义> > /LI> 或者正在通过volatile指针/引用访问非volatile限定的
-
< C++ >标准(通过导入;参阅注释),C++标准要求通过代码> >易失性*>代码>或<代码>易失性>代码>必须引用一个最初声明为“代码>易失性<代码>的对象,以便具有<代码>易失性< /代码>语义> <强> > /LI>
- 或者正在通过
指针/引用访问非volatile
限定的对象,该指针/引用足以/假定使所述访问行为如同对象被声明为volatile
?volatile
公认的智慧:引用的对象本身必须声明为volatile 昨天的热门问题是,假设一个
volatile
引用会赋予一个非volatile
引用对象volatile
行为,但发现它没有,或者以不同程度和不可预测的方式
接受的答案最初得出的结论是,只有所声明的引用对象类型才重要。这一点和大多数评论似乎都同意,正如我们对const
所熟知的那样,等效原则正在发挥作用:如果参考对象具有与参考对象相同的cv限定条件,则行为将是易变的
(或完全定义):
那篇文章的关键词是宾语<代码>易失性信号原子标志代码>是一个易失性对象*(volatile char*)foo
仅仅是通过volatile限定左值进行的访问,标准不要求该左值具有任何特殊效果
这一解释似乎被广泛接受,正如对这个类似但希望不是重复的问题的回答所示:但即使在那里也存在不确定性:在回答“不”之后,它会说“可能”!无论如何,让我们检查一下标准,看看“否”是基于什么
标准上说的。。。还是没有 C11,N1548,§6.7.3:很明显,通过不共享所述限定符的指针访问由
volatile
或const
类型定义的对象
6如果试图通过使用左值和非const
-限定类型来修改使用const
-限定类型定义的对象,则行为未定义。如果试图通过使用带有非限定类型的左值引用由限定类型定义的对象,则行为未定义。(133)
…标准似乎没有明确提到相反的场景,即volatile
。此外,在总结volatile
及其操作时,它现在讨论具有volatile
限定类型的对象:
7具有volatile
限定类型的对象可能会以实现未知的方式进行修改,或具有其他未知的副作用。因此,应严格按照5.1.2.3中所述的抽象机器规则对涉及此类对象的任何表达式进行评估。此外,在每个序列点上,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改。(134)构成对具有挥发性合格类型的对象的访问的是实现定义的
我们是否要假设“has”等同于“was defined with”?或者引用对象和引用限定符的组合
一位评论者用这样的措辞很好地总结了这个问题:
从n1548§6.7.3¨6中,标准使用短语“使用可变限定类型定义的对象”将其与“具有可变限定类型的左值”区分开来。不幸的是,这种“定义为”的对象与“左值”的区别没有延续,标准随后使用了“具有易失性限定类型的对象”,并说“构成对具有易失性限定类型的对象的访问的是实现定义的”(可以说是“左值”或“定义为”的对象)为清楚起见)。哦,好吧
同一节的第4段似乎不太经常被引用,但很可能是相关的,我们将在下一节中看到
合理怀疑:volatile
指针/引用是否打算在其解引用上赋予volatile
语义?
上述答案中有一条评论,作者引用了委员会先前的一项声明,该声明对“参考必须与参考匹配”的观点提出了质疑:
有趣的是,其中有一句话[C99关于volatile
]的基本原理,这意味着委员会的意思是*(volatile T*)x
强制将访问x
的人视为volatile;但该标准的实际措辞并不明确
void *secure_memset(void *v, int c , size_t n) {
volatile unsigned char *p = v;
while (n--) *p++ = c;
return v;
}
void *secure_memset(void *v, int c , size_t n) {
volatile unsigned char *p = v;
while (n--) *p++ = c;
return v;
}
#include <cstdint>
#include <cstring>
void * clearmem(void* p, std::size_t len)
{
auto vp = reinterpret_cast<volatile char*>(p);
while (len--) {
*vp++ = 0;
}
return p;
}
struct A
{
char sensitive[100];
A(const char* p)
{
std::strcpy(sensitive, p);
}
~A() {
clearmem(&sensitive[0], 100);
}
};
void use_privacy(A a)
{
auto b = a;
}
int main()
{
A a("very private");
use_privacy(a);
}
clearmem(void*, unsigned long):
leaq (%rdi,%rsi), %rax
testq %rsi, %rsi
je .L4
.L5:
movb $0, (%rdi)
addq $1, %rdi
cmpq %rax, %rdi
jne .L5
.L4:
xorl %eax, %eax
ret
use_privacy(A):
leaq -120(%rsp), %rax
leaq 100(%rax), %rdx
.L10:
movb $0, (%rax)
addq $1, %rax
cmpq %rdx, %rax
jne .L10
ret
main:
leaq -120(%rsp), %rax
leaq 100(%rax), %rdx
.L13:
movb $0, (%rax)
addq $1, %rax
cmpq %rdx, %rax
jne .L13
leaq -120(%rsp), %rax
leaq 100(%rax), %rdx
.L14:
movb $0, (%rax)
addq $1, %rax
cmpq %rdx, %rax
jne .L14
leaq -120(%rsp), %rax
leaq 100(%rax), %rdx
.L15:
movb $0, (%rax)
addq $1, %rax
cmpq %rdx, %rax
jne .L15
xorl %eax, %eax
ret