den*_*sbk 3 icu reinterpret-cast
我将代码从ICU 58.2移植到ICU 59.1,在那里他们将字符类型从uint16_t更改为char16_t.我打算直接进行reinterpret_cast,我需要转换类型,但发现ICU 59.1实际上提供了这种转换的功能.我不明白为什么他们需要在进行reinterpret_cast之前使用这个抗锯齿障碍.
#elif (defined(__clang__) || defined(__GNUC__)) && U_PLATFORM !=
U_PF_BROWSER_NATIVE_CLIENT
# define U_ALIASING_BARRIER(ptr) asm volatile("" : : "rm"(ptr) : "memory")
#endif
Run Code Online (Sandbox Code Playgroud)
...
inline const UChar *toUCharPtr(const char16_t *p) {
#ifdef U_ALIASING_BARRIER
U_ALIASING_BARRIER(p);
#endif
return reinterpret_cast<const UChar *>(p);
Run Code Online (Sandbox Code Playgroud)
为什么在不调用U_ALIASING_BARRIER的情况下使用reinterpret_cast是不安全的?
在猜测中,它是为了阻止任何违反严格别名规则的行为,这可能在调用尚未完全清理的代码时发生,从而在优化时导致意外行为(暗示这一点在上面的评论中:"障碍甚至跨函数边界进行指针抗锯齿优化.").
严格别名规则禁止解除引用指针,当它们具有不兼容的类型时,它们会使用相同的值(C语言概念,但C++会说更多单词的类似内容).这里有一个小的疑难杂症:char16_t和uint16_t是兼容不是必需的.uint16_t实际上是一个可选支持的类型(在C和C++中); char16_t相当于uint_least16_t,不一定是同一类型.它在x86上具有相同的宽度,但是编译器不需要将它标记为实际上是同一个东西.假设通常表明不同意图的类型可能是别名,它甚至可能是故意松懈的.
在链接的答案中有一个更完整的解释,但基本上给出了这样的代码:
uint16_t buffer[] = ...
buffer[0] = u'a';
uint16_t * pc1 = buffer;
char16_t * pc2 = (char16_t *)pc1;
pc2[0] = u'b';
uint16_t c3 = pc1[0];
Run Code Online (Sandbox Code Playgroud)
...如果由于某种原因编译器没有char16_t并uint16_t标记为兼容,并且您正在编译包含其等效的优化-fstrict-aliasing,则允许假设写入pc2无法修改任何pc1点,而不是在分配之前重新加载值c3,可能会给它u'a'代替.
有点像示例的代码可能会出现在转换过程的中途,其中前面的代码很乐意uint16_t *在任何地方使用,但现在char16_t *可以在块的顶部提供与ICU 59的兼容性,在下面的所有代码之前完全改为只通过正确输入的指针读取.
由于编译器通常不优化手动编码汇编,因此asm块的存在将强制它检查有关寄存器状态和其他临时值的所有假设,并在第一次取消引用之后对每个值执行完全重载.U_ALIASING_BARRIER,无论优化标志.如果您继续通过以下转换进行写操作uint16_t *(如果您这样做,这是合法的自己的错误),这将无法保护您免受任何进一步的别名问题,但它至少应该确保转换调用之前的状态不会保持一种可能导致写入新指针的方式随后被意外跳过.
| 归档时间: |
|
| 查看次数: |
119 次 |
| 最近记录: |