标签: strict-aliasing

取消引用类型惩罚指针将破坏严格别名规则

我使用以下代码来从文件中读取数据,作为更大程序的一部分.

double data_read(FILE *stream,int code) {
        char data[8];
        switch(code) {
        case 0x08:
            return (unsigned char)fgetc(stream);
        case 0x09:
            return (signed char)fgetc(stream);
        case 0x0b:
            data[1] = fgetc(stream);
            data[0] = fgetc(stream);
            return *(short*)data;
        case 0x0c:
            for(int i=3;i>=0;i--)
                data[i] = fgetc(stream);
            return *(int*)data;
        case 0x0d:
            for(int i=3;i>=0;i--)
                data[i] = fgetc(stream);
            return *(float*)data;
        case 0x0e:
            for(int i=7;i>=0;i--)
                data[i] = fgetc(stream);
            return *(double*)data;
        }
        die("data read failed");
        return 1;
    }
Run Code Online (Sandbox Code Playgroud)

现在我被告知使用-O2,我得到以下gcc警告: warning: dereferencing type-punned pointer will break strict-aliasing rules

谷歌我找到了两个正交的答案:

VS

c optimization gcc pointers strict-aliasing

45
推荐指数
4
解决办法
8万
查看次数

在 C++ 中,将结构体的标量成员视为构成数组是否有效?

在查看Dear Imgui的代码时,我发现了以下代码(为了相关性进行了编辑):

struct ImVec2
{
    float x, y;
    float& operator[] (size_t idx) { return (&x)[idx]; }
};
Run Code Online (Sandbox Code Playgroud)

很明显,这在实践中是有效的,但是从 C++ 标准的角度来看,这段代码合法吗?如果没有,任何主要编译器(G++、MSVC、Clang)是否提供任何显式或隐式保证该代码将按预期工作?

c++ struct pointers strict-aliasing undefined-behavior

42
推荐指数
4
解决办法
3864
查看次数

GCC别名检查w /限制指针

请考虑以下两个片段:

#define ALIGN_BYTES 32
#define ASSUME_ALIGNED(x) x = __builtin_assume_aligned(x, ALIGN_BYTES)

void fn0(const float *restrict a0, const float *restrict a1,
         float *restrict b, int n)
{
    ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a0[i] + a1[i];
}

void fn1(const float *restrict *restrict a, float *restrict b, int n)
{
    ASSUME_ALIGNED(a[0]); ASSUME_ALIGNED(a[1]); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a[0][i] + a[1][i];
}
Run Code Online (Sandbox Code Playgroud)

当我编译函数时,gcc-4.7.2 -Ofast -march=native -std=c99 -ftree-vectorizer-verbose=5 -S …

c optimization gcc strict-aliasing

35
推荐指数
1
解决办法
1602
查看次数

gcc,严格别名,并通过联合进行转换

你有任何恐怖故事要讲吗?GCC手册最近添加了一个关于-fstrict-aliasing的警告并通过联合转换指针:

[...]获取地址,强制生成指针并取消引用结果具有未定义的行为 [强调添加],即使转换使用了联合类型,例如:

    union a_union {
        int i;
        double d;
    };

    int f() {
        double d = 3.0;
        return ((union a_union *)&d)->i;
    }
Run Code Online (Sandbox Code Playgroud)

有没有人有一个例子来说明这种未定义的行为?

请注意,这个问题不是关于C99标准所说或不说的.它是关于gcc和其他现有编译器的实际功能.

我只是猜测,但一个潜在的问题可能在于设置d为3.0.因为d是永远不会直接读取的临时变量,并且永远不会通过"稍微兼容"的指针读取,所以编译器可能不会费心去设置它.然后f()将从堆栈中返回一些垃圾.

我的简单,天真,尝试失败了.例如:

#include <stdio.h>

union a_union {
    int i;
    double d;
};

int f1(void) {
    union a_union t;
    t.d = 3333333.0;
    return t.i; // gcc manual: 'type-punning is allowed, provided...' (C90 6.3.2.3)
}

int f2(void) {
    double d = 3333333.0;
    return ((union a_union *)&d)->i; // gcc …
Run Code Online (Sandbox Code Playgroud)

c gcc type-conversion strict-aliasing unions

34
推荐指数
3
解决办法
1万
查看次数

这真的打破了严格别名规则吗?

当我使用g ++编译这个示例代码时,我收到此警告:

警告:解除引用类型惩罚指针将破坏严格别名规则 [-Wstrict-aliasing]

代码:

#include <iostream>

int main() 
{
   alignas(int) char data[sizeof(int)];
   int *myInt = new (data) int;
   *myInt = 34;

   std::cout << *reinterpret_cast<int*>(data);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,不是data别名int,因此将其强制转换为int不会违反严格的别名规则?或者我在这里遗漏了什么?

编辑:奇怪,当我这样定义时data:

alignas(int) char* data = new char[sizeof(int)];
Run Code Online (Sandbox Code Playgroud)

编译器警告消失了.堆栈分配是否与严格别名产生差异?事实上它是一个char[]而不是一个char*意味着它实际上不能为任何类型别名吗?

c++ strict-aliasing language-lawyer

31
推荐指数
1
解决办法
1965
查看次数

是否错误地指定了严格别名规则?

正如先前建立的,形式的联合

union some_union {
    type_a member_a;
    type_b member_b;
    ...
};
Run Code Online (Sandbox Code Playgroud)

Ñ成员包括Ñ在重叠存储+ 1对象:联合本身一个目的并且对于每个联盟成员一个对象.很明显,您可以按任何顺序自由地读取和写入任何工会成员,即使读取的工会成员不是最后写入的工会成员.永远不会违反严格别名规则,因为访问存储的左值具有正确的有效类型.

脚注95 进一步支持了这一点,脚注95解释了类型双关语是否是联盟的预期用途.

严格别名规则启用的优化的典型示例是此函数:

int strict_aliasing_example(int *i, float *f)
{
    *i = 1;
    *f = 1.0;
    return (*i);
}
Run Code Online (Sandbox Code Playgroud)

编译器可能会优化到类似的东西

int strict_aliasing_example(int *i, float *f)
{
    *i = 1;
    *f = 1.0;
    return (1);
}
Run Code Online (Sandbox Code Playgroud)

因为它可以安全地假设写入*f不会影响值*i.

但是,当我们将两个指针传递给同一个联盟的成员时会发生什么?考虑这个例子,假设一个典型的平台float是IEEE 754单精度浮点数,并且int是32位二进制补码整数:

int breaking_example(void)
{
    union {
        int i;
        float f;
    } fi;

    return (strict_aliasing_example(&fi.i, &fi.f));
} …
Run Code Online (Sandbox Code Playgroud)

c strict-aliasing language-lawyer

30
推荐指数
4
解决办法
1634
查看次数

aligned_storage和严格别名

我目前正在使用aligned_storage来实现类似于boost :: optional的'Optional'类型.要做到这一点,我有一个类成员,如下所示:

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
Run Code Online (Sandbox Code Playgroud)

我使用placement new来创建对象,但是我不存储返回任何地方的指针.相反,我在所有我的成员函数中访问对象的基础类型(显然通过检查确保对象通过也存储在我的Optional类型中的布尔标志有效):

T const* operator->() const {
    return static_cast<T const*>(static_cast<void const*>(&t_));
}
Run Code Online (Sandbox Code Playgroud)

我的问题是这是否安全.我的理解是我对placement new的使用改变了对象的'动态类型',只要我继续使用该类型访问内存,我就没问题.但是我不清楚我是否必须保持从placement new返回的指针,或者我是否只允许在需要访问它时转换为底层类型.我已经阅读了C++ 11标准的第3.10节,但是我在标准方面还不够流利,无法确定.

如果可能的话,如果你能在答案中提到标准,我会感觉更好(这有助于我在晚上睡觉:P).

c++ strict-aliasing undefined-behavior language-lawyer type-punning

28
推荐指数
1
解决办法
1493
查看次数

所有指针都是从指向结构类型的指针派生的吗?

问题

是否所有指针都是从指向结构类型的指针派生的问题都不容易回答.我发现这是一个重要问题,主要有以下两个原因.

A.缺少指向"任何"不完整或对象类型的指针,对方便的函数接口施加了限制,例如:

int allocate(ANY_TYPE  **p,
             size_t    s);

int main(void)
{
    int *p;
    int r = allocate(&p, sizeof *p);
}
Run Code Online (Sandbox Code Playgroud)

[ 完整代码示例 ]

指向"任何"不完整或对象类型的现有指针明确描述为:

C99/ C11 §6.3.2.3 p1:

指向void的指针可以转换为指向任何不完整或对象类型的指针.[...]

从指向"任何"不完整或对象类型的现有指针派生的指针,指向void的指针,严格地是指向void的指针,并且不需要使用从指向"任何"不完整的指针派生的指针进行转换或对象类型.


B.程序员根据他们对特定实现的经验,使用基于不需要的假设的约定,有意识地或不知不觉地与指针的泛化相关,这种情况并不少见.假设,例如可转换,可表示为整数或共享公共属性:对象大小,表示或对齐.


标准的话

根据C99 §6.2.5 p27/ C11 §6.2.5 p28:

[...]所有指向结构类型的指针应具有相同的表示和对齐要求.[...]

其次是C99 TC3 Footnote 39/ C11 Footnote 48:

相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员.

虽然标准没有说:"指向结构类型的指针"并且选择了以下单词:"所有指向结构类型的指针",但它没有明确指定它是否适用于这种指针的递归推导.在标准中提到指针的特殊属性的其他情况下,它没有明确指定或提及递归指针派生,这意味着"类型派生"适用,或者它没有 - 但它没有明确提到.

虽然在引用类型时使用"所有指针"的措辞使用两次,(对于结构和联合类型),而不是更明确的措辞:在整个标准中使用的"指针",我们不能总结它是否适用于这种指针的递归推导.

c struct pointers c99 strict-aliasing

28
推荐指数
1
解决办法
812
查看次数

我应该采取什么预防措施来制作一个不会调用未定义行为的内存池?

我最初的问题是,在一个项目中,我有几个共享一生的对象(即,一旦我释放其中一个,我将释放它们全部),然后我想分配一个单独的内存块.我有三个不同的对象类型的数组,struct foo,void *,和char.起初我想要malloc()一个像这样的块:

// +---------------+---------+-----------+---------+---------+
// | struct foo[n] | padding | void *[m] | padding | char[o] |
// +---------------+---------+-----------+---------+---------+
Run Code Online (Sandbox Code Playgroud)

但那么......如果不调用未定义的行为,我怎么能做到这一点呢?即,尊重类型别名规则,aligment ...如何正确计算内存块大小,声明内存块(具有有效类型),以及如何正确地获取指向其中所有三个部分的指针?

(我明白我可以使用malloc()3个块,这会导致3个块,free()但我想知道如何使用单个块来执行它,同时仍然表现良好.)

我想将我的问题扩展到一个更普遍的问题:应该采取什么预防措施来为任意大小和对齐的对象实现内存池,同时保持程序的良好运行?(假设可以在不调用未定义行为的情况下实现它.)

c strict-aliasing undefined-behavior language-lawyer c11

28
推荐指数
3
解决办法
895
查看次数

允许使用char*对T*进行别名化.是否也允许反过来?

注意:此问题已重命名并缩小,以使其更具针对性和可读性.大多数评论都涉及旧文本.


根据标准,不同类型的对象可能不共享相同的存储位置.所以这不合法:

std::array<short, 4> shorts;
int* i = reinterpret_cast<int*>(shorts.data()); // Not OK
Run Code Online (Sandbox Code Playgroud)

但是,该标准允许此规则的例外:可以通过指向char或的指针访问任何对象unsigned char:

int i = 0;
char * c = reinterpret_cast<char*>(&i); // OK
Run Code Online (Sandbox Code Playgroud)

但是,我不清楚这是否也允许反过来.例如:

char * c = read_socket(...);
unsigned * u = reinterpret_cast<unsigned*>(c); // huh?
Run Code Online (Sandbox Code Playgroud)

c++ strict-aliasing language-lawyer type-punning

27
推荐指数
2
解决办法
3024
查看次数