2011-07-07 32 views
9

Tôi đã tìm thấy một đoạn mã tìm kiếm kỳ lạ trong một dự án mà tôi phải duy trì. Có một thành viên mảng trống của một lớp không dẫn đến lỗi trình biên dịch. Tôi đã thử nghiệm một số biến thể của mã như vậy với MSVC 10,0:Khai báo mảng trống - hành vi biên dịch lạ

template<class T> struct A { 
    int i[]; 
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct B { static int i[]; }; 
template<class T> int B<T>::i[]; 

struct C { 
    int i[]; 
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct D { static int i[]; }; 
template<class T> int D<T>::i[4]; 
template<>  int D<int>::i[] = { 1 }; 


int main() 
{ 
    A<void> a; 
    B<void> b; 
    C c; 
    D<void> d0; 
    D<int> d1; 

    a.i[0] = 0;  // warning C4739: reference to variable 'a' exceeds its storage space 

    b.i[0] = 0;  // warning C4789: destination of memory copy is too small 

    c.i[0] = 0;  // warning C4739: reference to variable 'c' exceeds its storage space 

    int i[];  // error C2133: 'i' : unknown size 

    d0.i[0] = 0; // ok 
    d0.i[1] = 0; // ok 

    return 0; 
} 

Thông báo lỗi tại int i[] là hoàn toàn hợp lý với tôi. Mã được hiển thị với lớp D là chuẩn C++ chuẩn. Nhưng còn các lớp học A, BC thì sao? Loại biến nào là biến thành viên int i[] trong lớp này?

Trả lời

6

EDIT:

nghi ngờ của bạn được giải thích bởi definition of the extension to the language, cho phép zero-kích thước mảng vào cuối của cấu trúc/đoàn. Tôi đã không thử nó, nhưng nếu bạn khai báo một thành viên khác sau khi mảng có kích thước bằng không, nó sẽ thất bại.

vì vậy, nếu bạn phân bổ một biến trên ngăn xếp, bạn phải biết kích thước của nó; ngoại lệ cho quy tắc là khi phân bổ một mảng ở cuối của một cấu trúc/công đoàn, trong đó một số thủ thuật C-điển hình là có thể.

Trong C++ điều này làm tăng một cảnh báo vì hàm tạo bản sao mặc định và toán tử gán có thể sẽ không hoạt động.

TRƯỚC ĐÁP:

Trình biên dịch cảnh báo bạn về một thực tế rằng bạn đang cố gắng xác định một mảng với kích thước không. Điều này không được phép trong tiêu chuẩn C/C++.

Hãy xem lớp khác biệt theo lớp.

Trong lớp D:

template<class T> struct D { static int i[]; };

nó hoạt động vì bạn chỉ khai báo kiểu của một biến thành viên tĩnh. Để liên kết này, bạn cũng cần xác định mảng thực tế, trong tuyên bố định nghĩa giống như bạn:

template<>  int D<int>::i[] = { 1 }; 

tại đây bạn cũng chỉ định kích thước của mảng thông qua bộ khởi tạo.

Với lớp B, bạn đang làm một cái gì đó tương tự, nhưng định nghĩa là:

template<class T> int B<T>::i[]; 

nghĩa là, bạn không chỉ định kích thước và nhận được cảnh báo.

Với lớp A, nhiều hơn giống nhau, bạn đang xác định biến thành viên của mảng loại không có kích thước.

+0

Câu hỏi đặt ra là: Tại sao các cảnh báo (liên quan đến cảnh báo 'A',' B' và 'C') thay vì lỗi? Theo tôi, điều này là không đối xứng so với lỗi tôi nhận được trên khai báo của biến cục bộ. – 0xbadf00d

+0

Xem chỉnh sửa của tôi, vui lòng. – sergio

+0

Cảm ơn, một phần mở rộng "đẹp" của Microsoft khác với tiêu chuẩn C++ ... – 0xbadf00d

0

Tốt nhất. Chỉ cần chắc chắn, bạn đang tự hỏi tại sao trình biên dịch không gắn cờ nó như là một lỗi đúng? Trong trường hợp đó, tôi nghĩ rằng vấn đề này là không thể đoán trước trên các trình biên dịch nhưng tôi biết điều này xảy ra trên MSVC mọi lúc.

http://support.microsoft.com/kb/98409

Hãy để tôi xem liệu tôi có thể giải thích nó như họ đã làm.Nếu tôi tuyên bố cấu trúc với một mảng trống như thế này, thì

struct a 
{ 
    int x; 
    char empty[]; 
}; 

trình biên dịch có thể phân bổ 4 byte cho x và có thể 4 byte khác cho con trỏ char. trống sẽ chứa địa chỉ 4 byte trong quá trình bắt đầu cấu trúc a.

Vì đó là mảng ký tự không có độ dài, cố gắng truy cập nó sẽ là lỗi do không có dấu 0 để biểu thị kết thúc chuỗi.

Tôi có thể chọn khởi tạo cấu trúc sau để trỏ đến đầu chuỗi thực tế để khắc phục lỗi này.

struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world" 

Vì cấu trúc cơ bản là một lớp, hóa ra bạn có thể làm điều tương tự với lớp nếu bạn đảm bảo tổng hợp và không phải là một lớp học đầy đủ.

Vì vậy, có ya. Trình biên dịch MSVC xử lý các mảng không có kích thước cố định như một con trỏ khi được khai báo trong một cấu trúc/lớp. Hãy nhớ rằng các định nghĩa lớp chỉ đơn thuần là các khai báo. Trình biên dịch không phân bổ không gian cho chúng cho đến khi bạn tạo một cá thể cho nó. Khi bạn bắt đầu suy nghĩ về nó, nó sẽ tạo ra từ đó. Làm thế nào trình biên dịch sẽ biết nếu bạn có kế hoạch phân bổ lưu trữ cho nó sau này. Nó trở thành một tạo phẩm thời gian chạy nhưng trình biên dịch vẫn đủ thông minh để cảnh báo bạn về vấn đề này.