2015-04-08 26 views
5

Tại sao những sản phẩm sau biên dịch mà không có lỗi ?:Biến số có thể được sử dụng khi được khai báo không?

int main() 
{ 
    int x = x; //I thought this should cause an error 
    return 0; 
} 

đâu trong các tiêu chuẩn là nó giải thích tại sao điều này cho phép?

+0

Bạn có thích câu trả lời cho C++ hoặc cho c? Có thể chúng khác nhau, xem xét chúng có các tiêu chuẩn khác nhau. – horns

+2

Đó là hành vi không xác định. – Maroun

+0

Một lý do chính đáng nữa để bật cảnh báo trình biên dịch: '-Wall -Werror' là bạn của bạn. – chqrlie

Trả lời

3

C++ 14 dự thảo N4140 [basic.scope.pdecl]/1:

Các điểm kê khai cho một tên là ngay sau khi declarator hoàn chỉnh của nó (khoản 8) và trước nó initializer (nếu có), ngoại trừ như được nêu dưới đây. [Ví dụ:

unsigned char x = 12; 
{ unsigned char x = x; } 

Ở đây, thứ hai x được khởi tạo với giá trị (không xác định) của riêng mình. dụ -end]

+0

Hầu hết những gì bạn đang trích dẫn là một lưu ý mà không phải là quy phạm và nó không thực sự giải thích cho dù đây là hành vi không xác định hay không. Xem [bình luận của tôi ở trên] (http://stackoverflow.com/questions/29525797/can-a-variable-be-used-while-being-declared#comment47210802_29525797) cho hai câu hỏi trả lời câu hỏi này cho C++ chi tiết cho cả hai trước C++ 14 và C++ 14 và chuyển tiếp. –

+0

@ShafikYaghmour: Câu hỏi không phải là liệu đây có phải là hành vi không xác định hay không, đó là liệu biến có thể được sử dụng trong quá trình khởi tạo riêng của nó hay không. Có rất nhiều trường hợp sử dụng biến mà không sử dụng giá trị của nó, ví dụ: 'void * p = & p; 'vì vậy nó là một câu hỏi hoàn toàn hợp lý. –

+0

@BenVoigt trong trường hợp C++ ví dụ này rõ ràng là hành vi không xác định và không nói như vậy là một câu trả lời nghèo IMHO. Chắc chắn có những lời cảnh báo nhưng không ai trong số họ được giải thích. Cách tối ưu hóa các trình biên dịch hiện đại, chúng ta cần phải rất rõ ràng về những gì và những gì không phải là UB b/c các trình tối ưu hóa có thể làm những điều rất tích cực với mã mà nó tin rằng gọi UB. –

3

Câu trả lời cho C++ - Tôi trích dẫn C++ 11 tiêu chuẩn:

3.3.3.1:

Một tên tuyên bố trong một khối (6.3) là địa phương cho khối đó; nó có phạm vi khối. Phạm vi tiềm năng của nó bắt đầu tại thời điểm khai báo (3.3.2) và kết thúc ở cuối khối của nó.

Và điểm kê khai được quy định tại 3.3.2 như

Điểm kê khai cho một tên là ngay sau khi declarator hoàn chỉnh của nó (khoản 8) và trước khi khởi tạo của nó (nếu có), ngoại trừ như được nêu dưới đây.

[Ví dụ:

int x = 12; 
{ int x = x; } 

Ở đây x thứ hai được khởi tạo với giá trị (không xác định) của riêng mình. - ví dụ kết thúc]

Rõ ràng, sử dụng giá trị x trước khi được khởi tạo là hành vi không xác định.

+2

"Sử dụng" 'x' có thể bao gồm' sizeof x' hoặc '& x'. Cả hai đều không rõ ràng là hành vi không xác định. – chux

+1

@chux Đồng ý - Tôi đã sửa đổi câu trả lời. –

+0

Có, tôi nghi ngờ ngay cả 'int x = x - x;' thậm chí không an toàn. – chux

6

Câu hỏi này có câu trả lời hơi khác trong C so với C++.

Trong cả hai trường hợp, int x = x; cố gắng khởi tạo x với chính nó.


Trong C++: [dcl.init]/12 (N3936) nói rằng bất kỳ đánh giá của một đối tượng có giá trị không xác định gây ra hành vi undefined, trừ một số trường hợp liên quan đến unsigned char.Trong thực tế có một Ví dụ:

int f(bool b) { 
    unsigned char c; 
    unsigned char d = c; // OK, d has an indeterminate value 
    int e = d;  // undefined behavior 
    return b ? d : 0; // undefined behavior if b is true 
} 

trong C: Nó phức tạp hơn. Điều này rất giống với hành vi của int b; foo(b - b);covered in full here.

tôi sẽ không lặp lại văn bản đó nhưng kết luận là, trong C11:

  • int a = a; &a; gây UB khi và chỉ khi hệ thống có cơ quan đại diện bẫy cho int
  • int a = a;, không có xảy ra tiếp theo của &a , gây ra UB.

Lưu ý lịch sử: Trong C90 điều này gây ra UB. Tại C99, đại diện bẫy đã được giới thiệu và trong C11 khả năng bẫy đăng ký đã được giới thiệu (đối với Itanium). Các tiêu chuẩn C + + không đối phó với đại diện bẫy ở tất cả, có vẻ như underspecified trong trường hợp của những thứ như bitwise operators tạo ra số không âm.

+1

Pascal cung cấp một bản tóm tắt rất hay về sự tiến hóa của các giá trị không xác định trong C trong [Đọc nội dung không xác định cũng có thể không được xác định] (http://blog.frama-c.com/index.php?post/2013/03/13/không xác định-undefined) –

+0

Hmm - không nên tuyên bố 'a' với từ khóa 'dễ bay hơi' làm cùng một công việc như một yêu cầu của địa chỉ biến (& a)? – AnArrayOfFunctions

+0

@FISOCPP tiêu chuẩn không nói rằng –

0

Có thực sự là hai phần cho câu hỏi này, là hỏi:

một biến thể được sử dụng trong khi được công bố?

nơi có câu trả lời rõ ràng là Có, do nguyên tắc khai báo được trích dẫn trong các câu trả lời khác.

Và, quan trọng không kém nhưng không hỏi:

gì tập quán của một biến trong khai của nó là an toàn không?

Vâng, trong bộ khởi tạo, biến chưa hoàn thành khởi tạo (một vấn đề về tuổi thọ đối tượng), và trong thực tế, xây dựng vẫn chưa bắt đầu. Các quy tắc đối tượng suốt đời (phần 3.8 của tiêu chuẩn) phát biểu rằng một số nhưng không phải tất cả các hoạt động được phép trên một biến như:

Trước khi cuộc đời của một đối tượng đã bắt đầu nhưng sau khi lưu trữ trong đó đối tượng sẽ chiếm đã được được cấp phát hoặc sau khi thời gian tồn tại của đối tượng đã kết thúc và trước khi lưu trữ đối tượng bị chiếm dụng hoặc được giải phóng, bất kỳ con trỏ nào trỏ đến vị trí lưu trữ nơi đối tượng sẽ được định vị hoặc chỉ có thể được sử dụng nhưng chỉ theo những cách hạn chế. Đối với một đối tượng đang được xây dựng hoặc phá hủy, xem 12.7. Nếu không, con trỏ như vậy đề cập đến bộ nhớ được phân bổ và sử dụng con trỏ như thể con trỏ thuộc loại void*, được xác định rõ. Việc chuyển hướng thông qua con trỏ như vậy được cho phép nhưng giá trị kết quả chỉ có thể được sử dụng theo những cách hạn chế, như được mô tả bên dưới.Chương trình này đã không xác định hành vi nếu:

  • đối tượng sẽ được hoặc là của một loại lớp học với một destructor không tầm thường và con trỏ được sử dụng như là toán hạng của một delete-ngôn luận,
  • con trỏ được sử dụng để truy cập thành viên dữ liệu không tĩnh hoặc gọi hàm thành viên không tĩnh của đối tượng hoặc
  • con trỏ được chuyển đổi hoàn toàn thành con trỏ đến lớp cơ sở ảo hoặc
  • con trỏ được sử dụng làm toán hạng a static_cast, ngoại trừ khi chuyển đổi là trỏ đến cvvoid, hoặc để con trỏ đến cvvoid và sau đó để con trỏ đến một trong hai cvchar hoặc cvunsigned char, hoặc
  • con trỏ được sử dụng như là toán hạng của một dynamic_cast.

có hiệu quả, với nhiều loại với khởi tạo không tầm thường, vị trí bộ nhớ không chứa một đối tượng, vì vậy nó không có loại năng động, và cố gắng truy cập nó như bất kỳ loại trừ char hoặc unsigned char ngay lập tức rơi afoul của bí danh nghiêm ngặt.

Đối với các loại có khởi tạo nhỏ, bao gồm int, một đối tượng tồn tại ngay sau khi lưu trữ được căn chỉnh phù hợp. Nhưng nếu bộ nhớ đó có thời lượng lưu trữ tự động hoặc động, giá trị sẽ không xác định cho đến khi biến được ghi vào. Quy tắc này từ mục 8.5 áp dụng:

Nếu không có bộ khởi tạo nào được chỉ định cho đối tượng, đối tượng được khởi tạo mặc định. Khi lưu trữ cho một đối tượng có thời lượng lưu trữ tự động hoặc động, đối tượng có giá trị không xác định và nếu không có khởi tạo được thực hiện cho đối tượng, đối tượng đó giữ lại giá trị không xác định cho đến khi giá trị đó được thay thế. [Lưu ý: Các đối tượng có thời gian lưu trữ tĩnh hoặc luồng không được khởi tạo, xem 3.6.2. - cuối note] Nếu một giá trị không xác định được sản xuất bởi một thẩm định, hành vi đó là undefined trừ trong các trường hợp sau đây:

và tất cả các trường hợp ngoại lệ được liệt kê là đặc trưng cho unsigned char.

Thoạt nhìn, quy tắc này dường như không áp dụng, bởi vì trình khởi tạo được chỉ định. Tuy nhiên, trong quá trình đánh giá bộ khởi tạo, chúng tôi chính xác trong trường hợp "Khi lưu trữ cho một đối tượng có thời lượng lưu trữ tự động hoặc động được thu được" trong đó quy tắc áp dụng.

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