2013-03-07 29 views
16

Lấy cảm hứng từ câu hỏi -5 một lần nữa!Trình biên dịch nghĩ gì về câu lệnh switch?

tôi đọc [this comment] của @Quartermeister và được ngạc nhiên!

Vì vậy, tại sao điều này biên dịch

switch(1) { 
    case 2: 
} 

nhưng điều này không.

int i; 

switch(i=1) { 
    case 2: // Control cannot fall through from one case label ('case 2:') to another 
} 

không

switch(2) { 
    case 2: // Control cannot fall through from one case label ('case 2:') to another 
} 

bản cập nhật này:

Câu hỏi -5 trở thành -3.

+1

Có thể trình biên dịch có thể tối ưu hóa chương trình đầu tiên, nhưng không thể xóa trình biên dịch thứ hai, vì nhiệm vụ này. –

+0

Đoán của tôi là '1' là một biểu thức liên tục được biết đến tại thời gian biên dịch, cho phép trình biên dịch tối ưu hóa toàn bộ chuyển đổi, trong khi' i = 1' là một biểu thức không liên tục (mặc dù cũng được biết đến trình biên dịch để tạo ra một giá trị cụ thể) do đó trình biên dịch cố gắng giữ nút gạt. – dasblinkenlight

+0

Trong một thế giới lý tưởng, trình biên dịch có lẽ nên chấp nhận hoặc từ chối cả hai. Nó chắc chắn không nên tạo ra bất kỳ mã cho họ. Nhưng trong thế giới này, tôi nghĩ đó chỉ là một ví dụ khác về "Hiệu ứng Forrest Gump:" Ngu ngốc cũng ngu ngốc ":) Hỏi: Tại sao lại lãng phí thời gian/braincell thậm chí thảo luận về nó? – paulsm4

Trả lời

21

Không ai trong số họ nên biên dịch. Đặc tả C# yêu cầu một phần chuyển đổi có ít nhất một câu lệnh. Trình phân tích cú pháp sẽ không cho phép nó.

Hãy bỏ qua thực tế là trình phân tích cú pháp cho phép danh sách câu lệnh trống; đó không phải là những gì có liên quan. Đặc điểm kỹ thuật nói rằng phần cuối của phần chuyển đổi không được có điểm kết thúc có thể truy cập; đó là bit liên quan.

Trong ví dụ cuối cùng của bạn, phần switch có một điểm cuối có thể truy cập:

void M(int x) { switch(2) { case 2: ; } } 

vì vậy nó phải là một lỗi.

Nếu bạn có:

void M(int x) { switch(x) { case 2: ; } } 

sau đó trình biên dịch không biết nếu x bao giờ sẽ được 2. Nó giả định thận trọng rằng nó có thể, và nói rằng phần có điểm cuối có thể truy cập, vì trường hợp chuyển đổi nhãn là có thể truy cập.

Nếu bạn có

void M(int x) { switch(1) { case 2: ; } } 

Sau đó, trình biên dịch có thể lý do đó các thiết bị đầu cuối là không thể truy cập bởi vì nhãn trường hợp là không thể truy cập. Trình biên dịch biết rằng hằng số 1 không bao giờ bằng hằng số 2.

Nếu bạn có:

void M(int x) { switch(x = 1) { case 2: ; } } 

hoặc

void M(int x) { x = 1; switch(x) { case 2: ; } } 

Sau đó, bạn biết và tôi biết rằng điểm cuối không phải là có thể truy cập, nhưng trình biên dịch không biết điều đó. Quy tắc trong đặc tả là khả năng tiếp cận chỉ được xác định bằng cách phân tích các biểu thức liên tục. Bất kỳ biểu thức nào có chứa biến, ngay cả khi bạn biết giá trị của nó bằng một số phương tiện khác, không phải là một biểu thức liên tục.

Trong quá khứ trình biên dịch C# có lỗi trong trường hợp này không phải như vậy. Bạn có thể nói những điều như:

void M(int x) { switch(x * 0) { case 2: ; } } 

và trình biên dịch sẽ lý do x * 0 phải bằng 0, do đó không thể truy cập nhãn trường hợp. Đó là một lỗi mà tôi đã sửa trong C# 3.0. Các đặc điểm kỹ thuật nói rằng chỉ chỉ hằng số được sử dụng cho phân tích đó, và x là một biến, không phải là một hằng số.

Bây giờ, nếu chương trình là hợp pháp thì trình biên dịch có thể sử dụng các kỹ thuật nâng cao như thế này để tác động đến mã được tạo ra. Nếu bạn nói điều gì đó như:

void M(int x) { if (x * 0 == 0) Y(); } 

Sau đó, trình biên dịch có thể tạo ra các mã như thể bạn đã viết

void M(int x) { Y(); } 

nếu nó muốn.Nhưng nó không thể sử dụng thực tế rằng x * 0 == 0 là đúng cho các mục đích xác định khả năng tiếp cận tuyên bố.

Cuối cùng, nếu bạn có

void M(int x) { if (false) switch(x) { case 2: ; } } 

sau đó chúng ta đều biết rằng việc chuyển đổi là không thể truy cập, do đó khối không có một điểm cuối có thể truy cập, vì vậy đây là, đáng ngạc nhiên, luật pháp. Nhưng với các cuộc thảo luận ở trên, bây giờ bạn biết rằng

void M(int x) { if (x * 0 != 0) switch(x) { case 2: ; } } 

không đối xử với x * 0 != 0 như false, vì vậy điểm cuối được coi là có thể truy cập.

+0

Cảm ơn bạn. Tôi đã thử nghiệm cho 'switch (x) {}', nó biên dịch, phải không? –

+0

@KenKin: Đó là hợp pháp; một công tắc được phép chứa các phần chuyển đổi bằng không. –

1

Được rồi, vì vậy vấn đề với điều này là trình biên dịch hoàn toàn tối ưu hóa đi công tắc, và đây là bằng chứng:

static void withoutVar() 
{ 
    Console.WriteLine("Before!"); 

    switch (1) 
    { 
     case 2: 
    } 

    Console.WriteLine("After!"); 
} 

nào, khi dịch ngược với ILSpy, cho chúng ta thấy IL này:

.method private hidebysig static 
    void withoutVar() cil managed 
{ 
    // Method begins at RVA 0x2053 
    // Code size 26 (0x1a) 
    .maxstack 8 

    IL_0000: nop 
    IL_0001: ldstr "Before!" 
    IL_0006: call void [mscorlib]System.Console::WriteLine(string) 
    IL_000b: nop 
    IL_000c: br.s IL_000e 

    IL_000e: ldstr "After!" 
    IL_0013: call void [mscorlib]System.Console::WriteLine(string) 
    IL_0018: nop 
    IL_0019: ret 
} // end of method Program::withoutVar 

Mà không có hồi ức của một tuyên bố chuyển đổi bất cứ nơi nào. Tôi nghĩ rằng rằng lý do nó không tối ưu hóa đi một thứ hai cũng có thể có một cái gì đó để làm với quá tải nhà điều hành và sắp xếp. Vì vậy, có thể tôi có một loại tùy chỉnh khi được gán cho 1, nó sẽ chuyển thành 2. Tuy nhiên, tôi không hoàn toàn chắc chắn, dường như với tôi như một báo cáo lỗi nên được gửi đi.

+0

"... tối ưu hóa vòng lặp," ... vòng lặp nào? Tôi nghĩ bạn có nghĩa là chuyển đổi. –

+0

@JimMischel điểm tốt, cố định. –

+2

Nó không phải là một lỗi; vui lòng không gửi báo cáo. –

2

Trong Visual Studio 2012, lý do đầu tiên là hiển nhiên. Trình biên dịch xác định rằng mã không thể truy cập được:

switch (1) 
{ 
    case 2: 
} 

Cảnh báo: Mã không thể truy cập được phát hiện.

Trong hai trường hợp còn lại, trình biên dịch báo cáo "Điều khiển không thể rơi từ một nhãn trường hợp ('trường hợp 2:') sang nhãn khác". Tôi không thấy nó nói "('case 1')" trong cả hai trường hợp không thành công.

Tôi đoán trình biên dịch không phải là tích cực về đánh giá liên tục. Ví dụ, sau đây là tương đương:

int i; 
switch(i=1) 
{ 
    case 2: 
} 

int i = 1; 
switch(i) 
{ 
    case 2: 
} 

Trong cả hai trường hợp, trình biên dịch cố gắng để tạo ra mã, khi nó thể làm việc đánh giá và xác định rằng những gì bạn đang viết là:

switch (1) 
{ 
    case 2: 
} 

Và xác định mã không thể truy cập được.

Tôi nghi ngờ câu trả lời "tại sao không biên dịch này" sẽ là "bởi vì chúng tôi cho phép trình biên dịch JIT xử lý tối ưu hóa tích cực".

+0

Cảm ơn bạn. Tôi sửa đổi. –

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