被有效的类型规则困惑

时间:2016-12-27 01:30:26

标签: c language-lawyer

我似乎再次错过了关于有效类型的几个难题......代码中的注释基本上是我的问题,但这是我能够用适当的上下文来思考这个问题的唯一方法。

#include <stdlib.h>
#include <string.h>

typedef struct foo {
    int n;
} Foo;

int main(void)
{
    // No effective type yet because there has been no write to the memory.
    Foo *f = malloc(sizeof(Foo));

    // Effective type of `f` is now `Foo *`, except I'm writing to
    // `f->n`, so shouldn't it be `int *`? Not sure what's going on here.
    f->n = 1;

    // No effective type yet because there has been no write to the memory.
    char *buf = malloc(sizeof(Foo));

    // Effective type of `buf` is `Foo *`, despite it being declared as
    // `char *`.
    // It's not safe to modify `buf` as a `char` array since the effective type
    // is not `char`, or am I missing something?
    memcpy(buf, f, sizeof(Foo));

    // The cast here is well defined because effective type of `buf` is
    // `Foo *` anyway, right?
    ((Foo *)buf)->n++;

    // I'm not even sure this is OK. The effective type of `buf` is `Foo *`,
    // right? Why wouldn't it be OK then?
    memcpy(f, buf, sizeof(Foo));

    // Safe if the last `memcpy` was safe.
    f->n++;

    // buf now points to invalid memory.
    free(buf);

    // Pointers with different declared types point to the same object,
    // but they have the same effective type that is not qualified by
    // `restrict`, so this doesn't violate strict aliasing, right?
    // This is allowed because `f` was allocated via `malloc`,
    // meaning it is suitably aligned for any data type, so
    // the effective type rules aren't violated either.
    buf = (void *)f;

    // `f`, and consequently `buf` since it points to the same object as `f`,
    // now point to invalid memory.
    free(f);
}

我认为所有这些都没问题,或者在某些情况下我错了吗?我意识到这是边界问多个问题,但我基本上是在询问我对有效类型和严格别名的理解是否正确。 GCC使用-pedantic-errors -Wextra -Wall -O2 -fstrict-aliasing -Wstrict-aliasing

为我制作了无诊断信息

3 个答案:

答案 0 :(得分:4)

你似乎对什么&#34;有效类型&#34;混淆了。适用于:它适用于malloc的空间,而不是任何指针。和C一样,指针是一个单独的对象,它与指针可能指向的任何空间具有不同的属性。

f是一个(命名)变量,因此其有效类型始终与其声明的类型相同,即Foo *。同样,buf的有效类型始终为char *。有效类型在运行时可能会更改的唯一时间是动态分配的空间。

您的要点和代码注释毫无意义,所以我决定重新注释您的代码。文本引用了每种情况下文本上方的代码:

Foo *f = malloc(sizeof(Foo));

行。已分配未初始化的字节,f指向它们。动态分配的空间尚无有效类型。

f->n = 1;

动态分配空间的第一个sizeof(int)字节的有效类型设置为int。 (* - 但见脚注)

char *buf = malloc(sizeof(Foo));
memcpy(buf, f, sizeof(Foo));

memcpy函数保留了复制的有效对象类型,因此sizeof(int)指向的空格的第一个buf字节的有效类型为int。< / p>

((Foo *)buf)->n++;

首先,强制转换没有对齐问题,因为malloc空格已针对任何类型正确对齐。转到n的访问权限,这是正常的,因为((Foo *)buf)->nint类型的左值,它指定了有效类型int的对象。所以我们可以毫无问题地进行读写。

memcpy(f, buf, sizeof(Foo));

memcpy总是正常,因为它设置了有效类型(您的注释表明memcpy在某些情况下可能不正常)。此行将f指向的空间的有效类型设置为int(因为源空间的有效类型为int)。

f->n++;

很好,与上面的((Foo *)buf)->n++基本相同。

free(buf);
buf = (void *)f;

冗余投射。 f指向的空间仍然有效类型int,因为这些行都没有写入该空间。

free(f);

没问题。

脚注:有些人对表达式f->n(或((Foo *)buf)->n wrt严格别名)采用不同的解释。他们说f->n定义为(*f).n,因此相关联有效类型是*f的类型,而不是f->n的类型。我不同意这种观点,因此我不会进一步阐述它。有针对C2X的建议澄清这种情况和严格别名的其他灰色区域。对于你的特定代码,代码在任何解释下仍然是正确的。

答案 1 :(得分:1)

// No effective type yet because there has been no write to the memory.
Foo *f = malloc(sizeof(Foo));

此处没有访问权限,也没有对象。有效类型无关。

// Effective type of `f` is now `Foo *`, except I'm writing to
// `f->n`, so shouldn't it be `int *`? Not sure what's going on here.
f->n = 1;

地址f处第一个sizeof(Foo)字节处的对象具有有效类型Foo,而地址f处第一个sizeof(int)字节处的对象具有有效类型int。

// No effective type yet because there has been no write to the memory.
char *buf = malloc(sizeof(Foo));

此处没有访问权限,也没有对象。有效类型无关。

// Effective type of `buf` is `Foo *`, despite it being declared as
// `char *`.
// It's not safe to modify `buf` as a `char` array since the effective type
// is not `char`, or am I missing something?
memcpy(buf, f, sizeof(Foo));

对象位于地址的第一个sizeof(Foo)字节但是,有一个有效的类型Foo,并且第一个sizeof(int)字节的对象在地址但是有效类型为int。

无论其有效类型如何,都可以使用字符类型访问任何对象。您可以使用char访问buf的字节。

// The cast here is well defined because effective type of `buf` is
// `Foo *` anyway, right?
((Foo *)buf)->n++;

是。整个表达式都有效。

// I'm not even sure this is OK. The effective type of `buf` is `Foo *`,
// right? Why wouldn't it be OK then?
memcpy(f, buf, sizeof(Foo));

这没关系。 memcpy将地址f处的对象类型更改为Foo类型。即使f之前没有Foo类型,它现在也可以。

// Safe if the last `memcpy` was safe.
f->n++;

是。

// buf now points to invalid memory.
free(buf);

// Pointers with different declared types point to the same object,
// but they have the same effective type that is not qualified by
// `restrict`, so this doesn't violate strict aliasing, right?
// This is allowed because `f` was allocated via `malloc`,
// meaning it is suitably aligned for any data type, so
// the effective type rules aren't violated either.
buf = (void *)f;

你混合概念。限制和单个指针的值与别名无关。访问是。指针buf现在只指向地址f。

// `f`, and consequently `buf` since it points to the same object as `f`,
// now point to invalid memory.
free(f);

是。

答案 2 :(得分:0)

代码是有效的 - 因为必须处理多态数据对象 - 是在C ++之前完成的。

然而,在一个人可能在脚下射击之前,可以从这些手术中推断出更多的东西。这可能发生在Foo并说出一个不同大小的Foo2类型,然后访问一个不存在的元素,因为关联的malloc()不够大。

通常,如果指针类型始终与malloc()相同,则更容易理解并且可能更正确。对于更高级的态射,c ++可能不易出错(只要其警告不被抑制)。