2016-12-27 107 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); 

是的。