2010-07-13 2 views
21
#include <stdio.h> 
int main() { 
    int c = c; 
    printf("c is %i\n", c); 
    return 0; 
} 

Tôi đang xác định biến số nguyên được gọi là c và tôi chỉ định giá trị của nó cho chính nó. Nhưng làm thế nào điều này thậm chí có thể biên dịch? c chưa được khởi tạo, vậy giá trị của nó có thể được gán cho chính nó như thế nào? Khi tôi chạy chương trình, tôi nhận được c is 0.Tại sao mã C này biên dịch?

Tôi giả định rằng trình biên dịch tạo mã lắp ráp là gán khoảng trống cho biến số c (khi trình biên dịch gặp phải câu lệnh int c). Sau đó, phải mất bất kỳ giá trị rác nào trong không gian chưa được khởi tạo đó và gán nó trở lại c. Đây có phải là những gì đang xảy ra không?

+3

Thật không may, nó được cho phép - bạn thường có thể làm cho trình biên dịch phát ra một cảnh báo về nó, ví dụ sử dụng g ++ (nhưng không phải gcc vì lý do nào đó) bằng cờ -Wall tạo cảnh báo. –

+7

@Neil, để buộc lỗi với gcc, bạn sử dụng '-Werror = uninitialized -Winit-self' –

Trả lời

30

Tôi nhớ trích dẫn câu trả lời này trong câu trả lời trước nhưng tôi không thể tìm thấy câu trả lời vào lúc này.

C++ 03 §3.3.1/1:

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

Do đó biến c có thể sử dụng được ngay cả trước phần khởi tạo.

Chỉnh sửa: Xin lỗi, bạn đã hỏi về C cụ thể; mặc dù tôi chắc rằng có một đường tương đương trong đó. James McNellis đã tìm thấy:

C99 §6.2.1/7: Bất kỳ số nhận dạng nào không phải là cấu trúc, công đoàn hoặc thẻ điều tra "đều có phạm vi bắt đầu ngay sau khi người khai báo hoàn thành." Người khai báo được theo sau bởi bộ khởi tạo.

+0

Phát ngay! '+ 1' ....... –

+0

Cảm ơn! Giải thích tuyệt vời! –

+12

C99 §6.2.1/7: Bất kỳ số nhận dạng nào không phải là một cấu trúc, công đoàn hoặc thẻ điều tra "có phạm vi bắt đầu ngay sau khi hoàn thành trình khai báo của nó." Người khai báo được theo sau bởi bộ khởi tạo. –

2

c đã được khởi tạo!

Mặc dù đây là một dòng mã, nhưng thực tế là khởi tạo c trước, sau đó gán c cho nó. Bạn thật may mắn khi trình biên dịch khởi tạo c bằng 0 cho bạn.

+0

-1 ...sai lầm và sai, nhưng vẫn được bình chọn cao nhất ... biến 'c' trong ví dụ được hiển thị không phải là con trỏ, nhưng được cấp phát trên ngăn xếp. – gnud

+4

Cái gì? Ai nói nó phải là một con trỏ? –

+1

Không đồng ý - 'c' là uninitialized cho tôi (tùy thuộc vào tùy chọn trình biên dịch), được chứng minh bằng khác nhau, không bằng không đầu ra khi chạy mã này. – pilcrow

5

Hành vi không xác định để sử dụng giá trị chưa được khởi tạo (§C99 J.2 "Giá trị của đối tượng có thời lượng lưu trữ tự động được sử dụng trong khi đó là không xác định"). Vì vậy, mọi thứ có thể xảy ra từ nasal demons tới c = 0, đến playing Nethack.

+0

Giá trị không xác định, nhưng đó không phải là hành vi không xác định. Mũi quỷ, và Nethack đã ra ngoài. c = 0 là có thể (và có khả năng). Hành vi ở đây được định nghĩa theo cách tương tự với trình tạo số ngẫu nhiên trong đó mặc dù chúng ta không thể xác định giá trị kết quả, chúng ta có thể được đảm bảo bởi tiêu chuẩn rằng giá trị là điều duy nhất không biết về câu lệnh này. –

11

Đoán của bạn là chính xác. int c đẩy không gian vào ngăn xếp cho biến, sau đó được đọc từ và viết lại cho phần c = c (mặc dù trình biên dịch có thể tối ưu hóa điều đó). Trình biên dịch của bạn đang đẩy giá trị trên là 0, nhưng điều này không được đảm bảo luôn là trường hợp.

+0

+1 Giải thích rõ ràng. –

+4

Nó có khả năng không đẩy 0, nó chỉ xảy ra là khung stack hiện có đã có 0 bộ nhớ. Tùy thuộc vào 0 là có tất nhiên không xác định. –

+0

+1 rất hữu ích! –

2

Đặc điểm kỹ thuật C không đảm bảo rằng các biến sẽ được khởi tạo thành 0, 0.0 hoặc "" hoặc "".

Đó là tính năng của trình biên dịch và không bao giờ bạn phải đẩy điều đó sẽ xảy ra.

Tôi luôn đặt IDE/Trình biên dịch của mình để cảnh báo về điều đó.