2016-06-04 25 views
6

Lấy ví dụ sau đây:Hành vi không xác định với tính năng nhập kiểu?

typedef struct array_struct { 
    unsigned char* pointer; 
    size_t length; 
} array; 

typedef struct vector_struct { 
    unsigned char* pointer; 
    // Reserved is the amount of allocated memory not being used. 
    // MemoryLength = length + reserved; 
    size_t length, reserved; 
} vector; 


// Example Usage: 
vector* vct = (vector*) calloc(sizeof(vector), 1); 
vct->reserved = 0; 
vct->length = 24; 
vct->pointer = (unsigned char*) calloc(arr->length, 1); 

array* arr = (array*) vct; 
printf("%i", arr->length); 
free(arr->pointer); 
free(arr); 

C dường như cấp phát bộ nhớ cho các thành viên struct theo thứ tự chúng được xác định trong struct. Điều này có nghĩa là nếu bạn bỏ vector -> array bạn sẽ vẫn nhận được kết quả tương tự nếu bạn thực hiện các thao tác trên array như bạn đã làm nếu bạn đã thực hiện trên vector vì chúng có cùng thành viên và thứ tự các thành viên.

Miễn là bạn chỉ giảm xuống từ vector -> array như thể array là loại chung cho vector bạn không nên gặp phải bất kỳ sự cố nào.

Hành vi này không xác định và không phù hợp mặc dù cấu trúc tương tự của các loại?

+4

Bạn đang giả định 'array' và' vector' có đệm cùng, mà tôi không nghĩ được đảm bảo. – Cornstalks

+0

Tại sao họ không? Các padding không nên khác nhau nếu họ có cấu trúc tương tự. Bất kỳ phần đệm bổ sung nào sẽ ở cuối, bên ngoài cấu trúc, nơi nó không quan trọng. – FatalSleep

+0

Tôi có thể thấy lý do tại sao điều này có thể là hành vi bất thường, nhưng không chắc chắn 100% nếu nó không xác định và có bất kỳ mối đe dọa thực sự nào. – FatalSleep

Trả lời

6

Đây là hành vi được xác định rõ nếu bạn cho phép loại bí danh (mà C không phải hầu hết các trình biên dịch làm, hoặc theo mặc định hoặc cờ biên soạn) và hành vi không xác định nếu bạn cấm loại kiểu này aliasing (thường được gọi là "aliasing nghiêm ngặt" vì các quy tắc khá nghiêm ngặt). Từ dự thảo N1570 của chuẩn C:

6.5.2.3

6 Một đảm bảo đặc biệt được thực hiện để đơn giản hóa việc sử dụng các công đoàn: nếu một liên minh chứa nhiều cấu trúc có chung một dãy ban đầu thường gặp (xem bên dưới), và nếu đối tượng công đoàn hiện có chứa một trong các cấu trúc này, thì được phép kiểm tra phần đầu chung của bất kỳ số nào trong số chúng ở bất kỳ đâu. Hai cấu trúc chia sẻ một chuỗi ban đầu chung nếu các thành viên tương ứng có các loại tương thích (và, đối với các trường bit, cùng độ rộng) cho một chuỗi gồm một hoặc nhiều thành viên ban đầu.

Đó phần là về công đoàn, nhưng để cho hành vi đó là quy phạm pháp luật trong các đoàn thể, nó hạn chế khả năng đệm và do đó đòi hỏi phải có hai cấu trúc để chia sẻ một bố cục chung và đệm ban đầu. Vì vậy, chúng tôi đã có mà đi cho chúng tôi.

Bây giờ, đối với răng cưa chặt chẽ, tiêu chuẩn nói:

6,5

7 Một đối tượng có trách nhiệm đã có giá trị được lưu trữ của nó chỉ được truy cập bởi một biểu thức vế trái có một trong các loại sau đây:

  • loại tương thích với loại đối tượng hiệu quả
  • [...]

A "kiểu tương thích" là:

6.2.7

1 Hai loại có loại tương thích nếu loại của họ đều giống nhau.

Nó tiếp tục giải thích rằng nhiều hơn và liệt kê một vài trường hợp có nhiều "phòng lung linh" hơn nhưng không có trường hợp nào áp dụng ở đây. Thật không may cho bạn, buck dừng lại ở đây. Đây là hành vi không xác định.

Bây giờ, có một điều bạn có thể làm để làm được việc này sẽ là:

typedef struct array_struct { 
    unsigned char* pointer; 
    size_t length; 
} array; 

typedef struct vector_struct { 
    array array; 
    size_t reserved; 
} vector; 
+1

Tôi chỉ muốn đăng báo giá tương tự, mặc dù tôi không đồng ý rằng chúng tôi có thể suy ra rằng những gì OP không được xác định rõ. Nhưng tôi đồng ý rằng nó nên làm việc trong thực tế. – alain

+0

Thịt ở đây "* của một ** hoặc nhiều hơn". – alk

+0

Tôi không thể nói nó được xác định rõ ràng - nhưng khi được biên dịch trong VS, nó hoạt động như một nét duyên dáng như tôi đã giả định. Tôi sẽ cần phải kiểm tra trên GCC. – FatalSleep

Các vấn đề liên quan