Tôi gặp phải vấn đề này, và một ngày tôi quyết định đào sâu vào nó.
Ngắn phi câu trả lời nhưng giải pháp thực dụng:
Một cách để làm việc xung quanh "vấn đề" này là sử dụng một dấu chấm phẩy, ;
, ngay sau dấu hai chấm của một tuyên bố case ...:
. Ví dụ, sử dụng ví dụ mà bạn cung cấp, nó có thể được "cố định" vì vậy nó biên dịch và hoạt động như bạn sẽ trực giác mong đợi nó:
case 1:; // <- Note semi-colon.
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
Câu trả lời dài:
Một số lịch sử: Trước đây, C chỉ cho phép bạn khai báo các biến "khối cục bộ" ở đầu khối, sau đó được theo sau bởi các câu lệnh khác nhau. C99 đã thay đổi mọi thứ để bạn có thể tự do trộn lẫn các khai báo và khai báo biến.
Trong ngữ cảnh ngữ pháp BNF C99, khai báo biến là declaration
và tuyên bố là statement
. A statement
có nghĩa là nhiều thứ, một trong số đó được gọi là compound-statement
, là khối { ... }
quen thuộc.Phần ...
là lỏng lẻo định nghĩa là zero or more
block-items
với block-item
được định nghĩa lỏng lẻo như either a
declaration
or a
statement
.
Vấn đề nằm trong cách một labeled-statement
(một nhãn goto, trường hợp nhãn, hoặc default:
, về cơ bản các ...:
báo cáo) được định nghĩa, đó là lỏng lẻo định nghĩa ...: zero or more
statements
. Nó không phải là, như một trực giác có thể mong đợi, zero or more
statements
or
declarations
. Việc sử dụng một số ;
ngay sau khi labeled-statement
s :
về cơ bản sẽ chấm dứt phần zero or more
statements
một phần của labeled-statement
. Điều này khiến ngữ pháp quay trở lại định nghĩa compound-statement
, cho phép "tuyên bố" tiếp theo là statement
hoặc declaration
.
Tôi chưa khảo sát xem đây có phải là sự vô tình vượt qua thông số ngôn ngữ C99 (trong điều kiện thực tế, lỗi trong tiêu chuẩn C99) hay là sự nhượng bộ thực tế đối với sự phức tạp trong việc viết ngữ pháp ngôn ngữ . Nếu bạn không quen với việc viết các ngữ pháp, bạn sẽ nhận thấy rằng lời giải thích ở trên cho phép đệ quy: A labeled-statement
có thể khớp với case 1: case 2: case 3:
. Trong các điều khoản quá đơn giản (1)
, một số loại đệ quy ngữ pháp rất đơn giản và "không rõ ràng", trong khi các loại khác phức tạp và "mơ hồ". Để đơn giản, hầu hết các công cụ ngôn ngữ sẽ chỉ xử lý trường hợp bất kỳ sự mơ hồ nào phải được giải quyết một cách xác định bằng cách xem không có gì hơn "mã thông báo tiếp theo". Tôi đề cập đến điều này chỉ vì trong khi điều này có vẻ trực giác giống như thiếu thông số C99, có thể có lý do thuyết phục, không rõ ràng tại sao điều này tồn tại ... và tôi không bận tâm nghiên cứu thêm về chủ đề này để tìm hiểu một trong hai cách.
(1)
Đây không phải là mô tả chính xác về mặt kỹ thuật, nhưng gần đúng cho những người không quen với các vấn đề liên quan.
EDIT:
Các giải pháp tôi đưa công trình vào "hầu hết" các trường hợp (trường hợp là "tập quán", không switch
case
s), nhưng nó thất bại trong một trường hợp: Điều này sẽ không hoạt động khi khai báo tuyên bố một C99 variable length array
, chẳng hạn như case 1:; void *ptrs[count];
Điều này là do trong C99 nó là một lỗi để "nhảy qua" tuyên bố của một VLA C99 đó là trong phạm vi từ vựng tương tự, nơi nhảy đã diễn ra. Trong những trường hợp này, bạn cần sử dụng case 1: { void *ptrs[count]; }
. Trong trường hợp này, phạm vi của ptrs
VLA sẽ kết thúc vào lúc đóng }
. Đây là phức tạp hơn nó đầu tiên xuất hiện vì sau đây là mã C hoàn toàn hợp pháp, mặc dù thoạt nhìn ai bằng trực giác nghĩ rằng nó không phải là:
switch(3){
case 0:
printf("case 0\n");
break;
case 1:;
int *ip = NULL;
printf("case 1\n");
break;
case 2:
{
int ia[6];
printf("case 2\n");
break;
case 3:
printf("case 3\n");
break;
default:
printf("default\n");
}
}
này biên dịch, và khi chạy, bản in case 3
.
Xem thêm: Wikipedia: Duffs Device
+1 Tuy nhiên, bạn có thể nếu bạn tạo phạm vi lồng nhau bằng cách sử dụng {...} để bọc các câu lệnh cho nhãn trường hợp. –