2016-12-27 111 views
3

我似乎错过了关于有效类型的几个难题......代码中的注释基本上是我的问题,但这是我能想到用适当方式提出这个问题的唯一方法上下文。困惑于有效的类型规则

#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

+2

'BUF =(无效*)F'始终是有效的(无论F'的'的类型),仅仅是因为严格的别名规则有一个例外,它允许一个'字符*'别名的任何其它类型。 – user3386109

回答

4

你似乎混淆了“有效类型”适用于什么:它适用于malloc'd空间,不适用于任何指针。与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

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

首先,由于malloc空间被用于任何类型的正确对齐铸造不具有对准问题。移动到n的访问,这是可以的,因为((Foo *)buf)->nint类型的左值,并且它指定有效类型为int的对象。所以我们可以读写没有问题。

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

memcpy总是好的,因为它设置了有效的类型(您的评论建议的memcpy可能无法在某些情况下,OK)。该行将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澄清这种情况和其他严格别名的灰色区域,对于你的特定代码,代码在解释虽然。

+0

我只想补充一点的malloc的返回值应该进行验证。并且'Foo * f = malloc(sizeof * f);'是一个好习惯。 – Stargateur

+0

@Stargateur同意。为了简便起见,我省略了验证,从我的问题,我使用的类型名称强调我正在处理分配,读取,和相同的大小写。抱歉不提这一点。 :-) –

+0

“他们说,'F-> N'被定义为'(* F).n' ......” 他们是错的。它是C++,它通过'*'和'.'的等价组合来定义' - >',而不是C.在C语言规范中,' - >'运算符是独立定义的。 – AnT

0

的代码是有效的,—具有处理多态数据对象—是它是如何在C之前完成++。

然而,在人们可能在脚下自我射击之前,从这些操作中推断出的东西并不多。这可能来自Foo并且说Foo2这是一个不同的大小,然后访问一个不存在的元素,因为相关的malloc()不够大。

通常,如果指针类型始终与malloc()相同,则它更容易理解并且可能是正确的。对于爱好者来说,C++可能不太容易出错(只要其警告不被抑制)。

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

此处没有访问权限,也没有对象。有效类型不相关。在第一的sizeof(富)

// 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; 

对象中的字节地址处女,具有有效的Foo类型,并且在所述第一的sizeof(int)的物体在字节地址f有一个有效的int类型。

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

这里没有访问权限,也没有对象。有效类型不相关。在第一的sizeof(富)

// 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)); 

对象中的字节地址,但是,有一个有效的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); 

是的。