2016-03-08 16 views
13

Trong mã sau đây, tại sao biến số i không được gán giá trị 1?Định nghĩa biến trong câu lệnh chuyển đổi

#include <stdio.h>  

int main(void) 
{ 
    int val = 0; 
    switch (val) {   
     int i = 1; //i is defined here 

     case 0: 
      printf("value: %d\n", i); 
      break; 
     default: 
      printf("value: %d\n", i); 
      break; 
    } 
    return 0; 
} 

Khi tôi biên dịch, tôi nhận được một cảnh báo về i không được khởi tạo mặc dù int i = 1; rằng khởi rõ ràng nó

$ gcc -Wall test.c 
warning: ‘i’ is used uninitialized in this function [-Wuninitialized] 
    printf("value %d\n", i); 
    ^

Nếu val = 0, sau đó đầu ra là 0.

Nếu val = 1 hoặc bất cứ điều gì khác, sau đó đầu ra cũng là 0.

Xin giải thích cho tôi tại sao biến i được khai báo nhưng không được định nghĩa bên trong công tắc. Đối tượng có mã định danh là i tồn tại với thời lượng lưu trữ tự động (trong khối) nhưng không bao giờ được khởi tạo. Tại sao?

+0

Tôi hỏi về bạn trước khi định nghĩa nhãn trường hợp bạn bè. Bởi vì tôi đang cố gắng sử dụng i là biến cục bộ bên trong switch. – sakthi

+3

Vui lòng không đóng dưới dạng bản sao C++. Tìm phiên bản C. – 2501

+2

Đây không phải là bản sao của bài đăng bởi @ user3121023 vì trong câu hỏi được liên kết, khai báo 'i' là ** bên trong ** một câu lệnh' case', vì vậy bạn chỉ cần quấn nó quanh dấu ngoặc đơn. Trong trường hợp này, việc khai báo 'i' nằm ngoài bất kỳ câu lệnh' case' nào, tôi không chắc chắn điều này là hợp lệ C. – Holt

Trả lời

4

Trong trường hợp khi val không bằng 0, việc thực thi sẽ nhảy trực tiếp đến mặc định nhãn. Điều này có nghĩa là biến số i, trong khi được xác định trong khối, không được khởi tạo và giá trị của nó không xác định.

6.8.2.4 Lệnh switch

  1. Một chuyển đổi nguyên nhân tuyên bố kiểm soát để chuyển đến, vào, hoặc qua báo cáo kết quả đó là cơ thể chuyển đổi, tùy thuộc vào giá trị của một biểu thức điều khiển, và khi có nhãn mặc định và các giá trị của bất kỳ nhãn trường hợp nào trên hoặc trong thân chuyển đổi. Một trường hợp hoặc nhãn mặc định chỉ có thể truy cập trong báo cáo chuyển đổi kèm theo gần nhất.
3

Thật vậy, bạn i được tuyên bố bên trong khối switch, vì vậy nó chỉ tồn tại bên trong switch. Tuy nhiên, khởi tạo của nó không bao giờ đạt được, vì vậy nó vẫn uninitialized khi val không phải là 0.

Nó là một chút như đoạn mã sau:

{ 
    int i; 
    if (val==0) goto zerovalued; 
    else goto nonzerovalued; 
    i=1; // statement never reached 
    zerovalued: 
    i = 10; 
    printf("value:%d\n",i); 
    goto next; 
    nonzerovalued: 
    printf("value:%d\n",i); 
    goto next; 
    next: 
    return 0; 
} 

trực giác, suy nghĩ của tuyên bố liệu như yêu cầu trình biên dịch cho một số vị trí (trên khung cuộc gọi trong ngăn xếp cuộc gọi của bạn hoặc trong sổ đăng ký hoặc bất kỳ thứ gì) và nghĩ về việc khởi tạo dưới dạng câu lệnh gán. Cả hai đều là các bước riêng biệt, và bạn có thể xem một khai báo khởi tạo trong C như int i=1; làm đường cú pháp cho khai báo thô int i; theo sau là việc gán khởi tạo i=1;.

(trên thực tế, mọi thứ hơi phức tạp hơn ví dụ: với int i= i!=i; và thậm chí phức tạp hơn trong C++)

1

dòng cho khởi của tôi biến int i = 1; không bao giờ được gọi vì nó không thuộc về bất kỳ trường hợp nào có sẵn.

+2

Việc khởi tạo không cần phải được "gọi" để xảy ra. Hãy suy nghĩ về các biến toàn cầu. –

12

Theo tiêu chuẩn C (6.8 Báo cáo và khối), nhấn mạnh mỏ:

3 Một khối cho phép tập hợp các tuyên bố và câu lệnh được nhóm thành một đơn vị cú pháp. Các initializers của các đối tượng có thời gian lưu trữ tự động, và các mảng chiều dài biến declarators của định danh thông thường với phạm vi khối, được đánh giá và các giá trị được lưu trữ trong các đối tượng (bao gồm lưu trữ một giá trị không xác định trong các đối tượng mà không có một initializer) mỗi lần tuyên bố là đạt được theo thứ tự thực hiện, như thể đó là tuyên bố, và trong mỗi tuyên bố theo thứ tự mà người khai báo xuất hiện.

Và (6.8.4.2 Việc chuyển đổi tuyên bố)

4 Một chuyển đổi nguyên nhân tuyên bố kiểm soát để chuyển đến, vào, hoặc qua báo cáo kết quả đó là cơ thể chuyển đổi, tùy thuộc vào giá trị của một biểu thức kiểm soát và trên nhãn có nhãn mặc định và các giá trị của bất kỳ nhãn trường hợp nào trên hoặc trong thân chuyển đổi. Một trường hợp hoặc nhãn mặc định chỉ có thể truy cập trong báo cáo kèm theo gần nhất.

Như vậy initializer biến i không bao giờ được đánh giá vì việc kê khai

switch (val) {   
     int i = 1; //i is defined here 
     //... 

không đạt được theo thứ tự thực hiện do nhảy đến trường hợp nhãn và giống như bất kỳ biến với thời hạn lưu trữ tự động có không xác định giá trị.

cũng dụ bản quy phạm này Xem từ 6.8.4.2/7:

VÍ DỤ Trong chương trình mảnh vỡ nhân tạo

switch (expr) 
{ 
    int i = 4; 
    f(i); 

case 0: 
    i = 17; /* falls through into default code */ 
default: 
    printf("%d\n", i); 
} 

đối tượng có nhận dạng là i tồn tại với thời hạn lưu trữ tự động (bên trong khối) nhưng không bao giờ được khởi tạo, và do đó nếu biểu thức kiểm soát có giá trị không đồng bộ, lệnh gọi hàm printf sẽ truy cập vào một indetermina te giá trị. Tương tự, không thể liên lạc được với lệnh gọi hàm f.

+1

Câu trả lời hay, tôi nghĩ rằng phần từ 6.8 là cần thiết để giải thích điều này. Bạn có thể cho mỗi cơ hội lấy ví dụ tôi trích dẫn trong câu trả lời của tôi và thêm nó vào câu trả lời của bạn quá? (Vì nó là một ví dụ tiêu chuẩn. Hãy thoải mái sao chép/dán các công cụ định dạng từ câu trả lời của tôi) Sau đó, tôi sẽ xóa câu trả lời của tôi có lợi cho bạn. – Lundin

+0

Tôi nghĩ, phần quan trọng là "như thể nó là một tuyên bố". Nó nên nổi bật hơn nữa. –

+1

Trên thực tế, mọi câu trả lời không đề cập đến thời lượng lưu trữ tự động là không đầy đủ. Bởi vì nếu bạn khai báo 'i' là' static' thì thì đấy, đột nhiên mã hoạt động như mong đợi. Có nghĩa là điều này không liên quan nhiều đến hành vi của câu lệnh switch, như với quy tắc khởi tạo các biến với thời gian lưu trữ tự động. – Lundin

1

Việc khởi của các biến với thời hạn lưu trữ tự động là chi tiết trong C11 6.2.4p6:

  1. Đối với một đối tượng như vậy mà không có một loại mảng chiều dài thay đổi, tuổi thọ của pin mở rộng từ mục nhập vào khối mà nó được liên kết cho đến khi thực hiện khối đó kết thúc bằng bất kỳ cách nào. (Nhập một khối kín hoặc gọi một hàm bị đình chỉ, nhưng không kết thúc, thực hiện khối hiện tại.) Nếu khối được nhập đệ quy, một thể hiện mới của đối tượng được tạo ra mỗi lần. Giá trị ban đầu của đối tượng là không xác định.Nếu một khởi tạo được chỉ định cho đối tượng, nó được thực hiện mỗi lần khai báo hoặc hợp chất theo nghĩa đen đạt được trong việc thực thi khối; nếu không, giá trị sẽ trở thành không xác định mỗi lần khai báo.

I.e. tuổi thọ của i trong

switch(a) { 
    int i = 2; 
    case 1: printf("%d",i); 
      break; 
    default: printf("Hello\n"); 
} 

{-}. Giá trị của nó là không xác định, trừ khi khai báo int i = 2; trong việc thực thi khối. Kể từ khi tuyên bố là trước khi bất kỳ nhãn trường hợp, tuyên bố không thể bao giờ đạt được, kể từ khi switch nhảy đến nhãn trường hợp tương ứng - và hơn khởi tạo.

Do đó i vẫn chưa được khởi tạo. Và kể từ khi nó làm, và vì nó có địa chỉ của nó không bao giờ được thực hiện, việc sử dụng các giá trị chưa được khởi tạo để hành vi undefinedC11 6.3.2.1p2:

  1. [...] Nếu vế trái chỉ định đối tượng của thời gian lưu trữ tự động có thể được khai báo với lớp lưu trữ đăng ký (không bao giờ có địa chỉ của nó) và đối tượng đó chưa được khởi tạo (không khai báo với bộ khởi tạo và không gán cho nó đã được thực hiện trước khi sử dụng), hành vi là không xác định.

(Chú ý rằng các tiêu chuẩn riêng của mình ở đây từ những nội dung trong ngoặc làm rõ sai - đó là tuyên bố với một initializer nhưng initializer không được thực hiện).

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