2010-07-29 66 views
6

Tôi quan tâm nếu có bất kỳ sự khác biệt từ C hoặc C++ quan điểm biên dịch cho dù tôi sử dụng:Có sự khác biệt nào trong C và C++ giữa việc sử dụng if, else if, else if, ... và sử dụng switch() {case A: ... case B: ...}?

if (value == a) { 
    ... 
} 
else if (value == b) { 
    ... 
} 
else if (value == c) { 
    ... 
} 

so

switch (value) { 
    case a: 
     ... 
     break; 
    case b: 
     ... 
     break; 
    case c: 
     ... 
     break; 
} 

Nó cảm thấy với tôi rằng không có sự khác biệt, chỉ là cú pháp. Có ai biết thêm về nó không?

Cảm ơn, Boda Cydo.

Trả lời

6

Có sự khác biệt - với công tắc, trình biên dịch có thể tối ưu hóa công tắc để sử dụng bảng tra cứu. Điều này có thể xảy ra nếu có nhiều giá trị đủ gần nhau. Ví dụ, công tắc này:

switch (integer) { 
    case 10: 
    xxx 
    break; 
    case 12: 
    yyy 
    break; 
    case 13 
    zzz 
    break; 
} 

có thể trở thành (giả):

address = lookup[ integer - 10 ]; // which is prefilled with { case_10, err, err, case_12, case 13 } 
goto address; 
case_10: xxx; goto err; 
case_12: yyy; goto err; 
case_13: zzz; 
err: //do nothing 
+0

Với 'if's, trong một số trường hợp nhất định, trình biên dịch có thể tối ưu hóa cây' if' để sử dụng bảng tra cứu. Điều này ít có khả năng hơn, với các trình biên dịch hiện tại, nhưng nó không vượt quá những gì mà Standard nói. –

0

Chuyển đổi phải được biên dịch thành bước nhảy có địa chỉ gián tiếp, trong khi một chuỗi các câu lệnh if sẽ là một chuỗi các bước nhảy có điều kiện. Đầu tiên là thời gian không đổi; tất nhiên, bạn có thể đặt nhiều điều kiện chung hơn trong một nếu.

Chỉnh sửa: Tôi nên đề cập đến rằng tôi sẽ không ngạc nhiên nếu một số trình biên dịch thông minh có thể phát hiện rằng tất cả các điều kiện trong một chuỗi các if có một dạng đơn giản cụ thể và chuyển đổi thành một nút chuyển. Tôi không biết nếu họ làm, nhưng bạn luôn có thể dịch ngược và kiểm tra.

8

Có, có sự khác biệt. Việc xếp hạng bảo đảm của các điều kiện theo thứ tự được xếp theo thứ tự if s. Công tắc này chỉ đảm bảo một đánh giá duy nhất về bất kỳ thứ gì được sử dụng làm thông số chuyển đổi. Tùy thuộc vào trình biên dịch, chuyển đổi thường sẽ mất gần (gần) thời gian liên tục bất kể nhánh đã chọn, trong khi thác if đảm bảo rằng chân đầu tiên là nhanh nhất, giây thứ hai nhanh nhất, v.v ... .

+2

Nó không phải là khá đơn giản. Nếu điều kiện không có tác dụng phụ (như trong OP), trình biên dịch có thể sắp xếp lại một if/else khá dễ dàng. Ví dụ: [gcc] (http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Optimize-Options.html#Optimize-Options) có thể thực hiện điều này với các tùy chọn như '-freorder-blocks' và '-fguess-branch-probability'. Trình biên dịch cũng có thể tạo ra một bảng nhảy từ một thác nếu trong một số trường hợp nhất định. Cuối cùng, các câu lệnh 'switch' sẽ không dẫn đến một bảng nhảy nếu các giá trị quá thưa thớt. –

1

Điều này sẽ phụ thuộc vào cách trình biên dịch chọn tối ưu hóa mã của bạn. Tối ưu hóa mã cho trình biên dịch là một lĩnh vực rất lớn.

Để tìm câu trả lời chính xác cho trình biên dịch của bạn, hãy xác định cách xây dựng mã lắp ráp và xem mã lắp ráp khác nhau được ghi vào tệp.

Điều này đã được thực hiện với một trình biên dịch và bạn có thể xem kết quả tại đây. http://www.eventhelix.com/RealtimeMantra/Basics/CToAssemblyTranslation3.htm

Nhưng câu trả lời ngắn gọn là có. chúng rất có thể sẽ khác.

0

Những tuyên bố trường hợp có thể được biên dịch với một "nhảy bảng" mà có thể nhanh hơn nếu có hàng chục trường hợp và bạn thực hiện hàng triệu lần này.

0

Có thể, nếu giá trị chọn là số nguyên (mà nó phải ở trong C/C++), thì trình biên dịch có thể thay thế nếu có bằng một bảng nhảy.

3

Có một số khác biệt, theo tiêu chuẩn.

  1. value có thể được đánh giá nhiều lần trong chuỗi if, một lần trong câu hỏi switch. Nếu đánh giá value không có tác dụng phụ, điều này là không quan trọng.
  2. Chuỗi if sẽ không cho phép bỏ qua, trong khi tuyên bố switch sẽ có tỷ lệ bỏ qua mà không có break.
  3. Chuỗi if cho phép so sánh chung, nhưng câu lệnh switch chỉ cho phép so sánh với biểu thức không thể thiếu.
  4. Việc sử dụng break; là khác nhau. Nó vỡ ra khỏi tuyên bố switch, nhưng thêm nữa. Nếu bạn cần thoát khỏi vòng lặp hoặc kèm theo tuyên bố switch tùy thuộc vào điều kiện, bạn cần chuỗi if.
  5. Vì tuyên bố switch thực chất là goto cho câu hỏi case hoặc default:, nó sẽ hoạt động ở các vị trí khác nhau, ví dụ tinh túy là Duff's device. (IIRC, Tom Duff coi nó một cuộc tranh luận mạnh mẽ trong câu hỏi của fallthrough, nhưng ông không chắc chắn về phía nào.)

Vì vậy, nếu value được đánh giá không có tác dụng phụ, break; báo cáo được sử dụng một cách nhất quán và chỉ trong switch, so sánh là các giá trị tích phân không đổi, và nó không được sử dụng một cách sôi nổi, hành vi có thể giống hệt nhau. Cho dù bất kỳ trình biên dịch sẽ sử dụng tương đương này là một câu hỏi khác.

0

+1 cho câu trả lời của David Thomley, vì tôi thực sự thấy điều này hoàn chỉnh nhất.

Một điều quan trọng là mất tích mặc dù, đó là case nhãn phải là biểu thức liên tục đánh giá tại thời gian biên dịch. Với if cả hai mặt của so sánh (nếu bạn giảm tuyên bố if về điều đó) được đánh giá vào thời gian chạy.

1

tôi đến cùng một vấn đề vì vậy tôi đã làm một vài xét nghiệm, đây là một số kết quả thu được sử dụng gcc phiên bản 3.4.6/centos 4

ac và sử dụng cc IFS, nhưng cc mất biến từ dòng lệnh để trình biên dịch không biết giá trị của "b" tại thời gian biên dịch. TC sử dụng chuyển

mã nguồn:

a.c

#include <stdint.h> 
int main(){ 
uint32_t i,b=10,c; 
    for(i=0;i<1000000000;i++){ 
     if(b==1) c=1; 
     if(b==2) c=1; 
     if(b==3) c=1; 
     if(b==4) c=1; 
     if(b==5) c=1; 
     if(b==6) c=1; 
     if(b==7) c=1; 
    } 
} 

trước Chúa

#include <stdint.h> 
int main(){ 
uint32_t i,b=10,c; 
    for(i=0;i<1000000000;i++){ 
     switch(b){ 
     case 1: 
       c=1; 
       break; 
     case 2: 
       c=1; 
       break; 
     case 3: 
       c=1; 
       break; 
     case 4: 
       c=1; 
       break; 
     case 5: 
       c=1; 
       break; 
     case 6: 
       c=1; 
       break; 
     case 7: 
       c=1; 
       break; 
     } 
    } 
} 

c.c

#include <stdint.h> 
int main(int argc, char **argv){ 
uint32_t i,b=10,c; 

    b=atoi(argv[1]); 
    for(i=0;i<1000000000;i++){ 
     if(b==1) c=1; 
     if(b==2) c=1; 
     if(b==3) c=1; 
     if(b==4) c=1; 
     if(b==5) c=1; 
     if(b==6) c=1; 
     if(b==7) c=1; 
    } 
} 

đầu tiên chúng tôi biên dịch chương trình không có cờ tối ưu:

[email protected] ~ # gcc a.c -o a;gcc b.c -o b;gcc c.c -o c 
[email protected] ~ # time ./a 

real 0m4.871s 
user 0m4.866s 
sys  0m0.005s 
[email protected] ~ # time ./b 

real 0m1.904s 
user 0m1.904s 
sys  0m0.000s 
[email protected] ~ # time ./c 10 

real 0m4.848s 
user 0m4.836s 
sys  0m0.009s 

Kết quả như tôi nghĩ, chuyển đổi nhanh hơn nếu không sử dụng tối ưu hóa trình biên dịch.

Bây giờ chúng ta biên dịch sử dụng -O2:

[email protected] ~ # gcc a.c -o a -O2;gcc b.c -o b -O2;gcc c.c -o c -O2 
[email protected] ~ # time ./a 

real 0m0.055s 
user 0m0.055s 
sys  0m0.000s 
[email protected] ~ # time ./b 

real 0m0.537s 
user 0m0.535s 
sys  0m0.001s 
[email protected] ~ # time ./c 10 

real 0m0.056s 
user 0m0.055s 
sys  0m0.000s 

Surprise bất ngờ, cả hai chương trình (a.c và c.c) sử dụng IFS là nhanh hơn so với chuyển đổi (khoảng 10 lần!).

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