可以通过将变量标记为volatile来修复不安全的类型修饰吗? [英] Can unsafe type punning be fixed by marking a variable volatile?

查看:242
本文介绍了可以通过将变量标记为volatile来修复不安全的类型修饰吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在zwol对的回答中,通过在一个结构(另一个结构的子集)之间强制转换指针而不是第一个结构来实现C继承是否合法?成员?他举例说明了为什么在类似结构之间进行简单的类型转换并不安全,并且在注释中有一个示例环境,该行为在其中表现出异常:在-O2上用gcc编译以下内容导致其打印"x = 1.000000 some = 2.000000"

In zwol's answer to Is it legal to implement inheritance in C by casting pointers between one struct that is a subset of another rather than first member? he gives an example of why a simple typecast between similar structs isn't safe, and in the comments there is a sample environment in which it behaves unexpectedly: compiling the following with gcc on -O2 causes it to print "x=1.000000 some=2.000000"

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

struct base
{
    double some;
    char space_for_subclasses[];
};
struct derived
{
    double some;
    int value;
};

double test(struct base *a, struct derived *b)
{
    a->some = 1.0;
    b->some = 2.0;
    return a->some;
}

int main(void)
{
    size_t bufsz = sizeof(struct base);
    if (bufsz < sizeof(struct derived)) bufsz = sizeof(struct derived);
    void *block = malloc(bufsz);

    double x = test(block, block);
    printf("x=%f some=%f\n", x, *(double *)block);
    return 0;
}

我在鬼混代码以更好地准确理解它的行为,因为我需要做类似的事情,并且注意到将a标记为volatile足以防止其打印不同的值.这与我对发生问题的期望一致-gcc假设a->some不受写到b->some的影响.但是,我本以为gcc只能假设ab

I was fooling around with the code to better understand exactly how it behaves because I need to do something similar, and noticed that marking a as volatile was enough to prevent it from printing different values. This lines up with my expectations as to what is going wrong - gcc is assuming that a->some is unaffected by the write to b->some. However, I would have thought gcc could only assume this if a or b were marked with restrict.

我是否误解了这里发生的事情和/或限制限定符的含义?如果不是,gcc是否可以自由地做这个假设,因为 ab是不同类型的?最后,将ab都标记为volatile是否使该代码符合标准,或者至少防止未定义的行为允许gcc做出上述假设?

Am I misunderstanding what is happening here and/or the meaning of the restrict qualifier? If not, is gcc free to make this assumption because a and b are of different types? Finally, does marking both a and b as volatile make this code compliant with the standard, or at least prevent the undefined behaviour from allowing gcc to make the aforementioned assumption?

推荐答案

如果仅使用volatile限定的左值访问存储区域,则编译器将不得不走得很远,以免处理每次写入转换为写入到位模式的值并将其存储,每次读取都相当于从内存中读取位模式并将其转换为值.该标准实际上并没有强制要求这种行为,并且在理论上,编译器给出了以下信息:

If a region of storage is accessed exclusively using volatile-qualified lvalues, a compiler would have to go extremely far out of its way not to process every write as translating the values written to a pattern of bits and storing it, and every read as reading a bit pattern from memory and translating it into a value. The Standard does not actually mandate such behavior, and in theory a compiler given:

long long volatile foo;
...
int test(void)
{
  return *((short volatile*)(&foo));
}

可以假定任何可以调用test的代码分支都将永远不会执行,但是我还不知道任何以这种极端方式运行的编译器.

could assume that any code branch that could call test will never be executed, but I don't yet know of any compilers that behave in such extreme fashion.

另一方面,给出如下功能:

On the other hand, given a function like the following:

void zero_aligned_pair_of_shorts(uint16_t *p)
{
  *((uint32_t void volatile*)&p) = 0;
}

gccclang这样的

编译器将无法可靠地识别出它可能会对使用类型为uint16_t的非限定左值访问的对象的存储值产生某些影响.诸如icc之类的某些编译器将volatile访问视为同步已获取其地址的所有寄存器缓存对象的指示符,因为这样做是编译器遵循标准章程和附录C中描述的C精神的一种廉价便捷的方法.基本原理文档为不要阻止程序员完成需要做的事情",而无需特殊的语法.但是,其他编译器(例如gcc和clang)要求程序员要么使用gcc/clang特定的内在函数,要么使用命令行选项来全局阻止大多数形式的寄存器缓存.

compilers like gcc and clang will not reliably recognize that it might have some effect upon the stored value of an object which is accessed using an unqualified lvalue of type uint16_t. Some compilers like icc regard volatile accesses as an indicator to synchronize any register-cached objects whose address has been taken, because doing so it a cheap and easy way for compilers to uphold the Spirit of C principle described in the Standards' charter and rationale documents as "Don't prevent the programmer from doing what needs to be done" without requiring special syntax. Other compilers like gcc and clang, however, require that programmers either use gcc/clang-specific intrinsics or else use command-line options to globally block most forms of register caching.

这篇关于可以通过将变量标记为volatile来修复不安全的类型修饰吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆