Như những người khác đã nói, vấn đề không nằm ở goto
chính nó; vấn đề là ở cách mọi người sử dụng goto
và cách nó có thể làm cho mã khó hiểu và duy trì hơn.
Giả sử đoạn mã sau:
i = 4;
label: printf( "%d\n", i );
Giá trị nào được in cho i
? Khi nào nó được in? Cho đến khi bạn tính đến mọi trường hợp goto label
trong chức năng của mình, bạn không thể biết. Sự hiện diện đơn giản của nhãn đó sẽ phá hủy khả năng gỡ lỗi mã của bạn bằng cách kiểm tra đơn giản. Đối với các hàm nhỏ có một hoặc hai nhánh, không có vấn đề gì nhiều. Đối với các chức năng không nhỏ ...
Quay trở lại vào đầu những năm 90, chúng tôi đã nhận được một đống mã C điều khiển màn hình đồ họa 3d và được yêu cầu làm cho nó chạy nhanh hơn. Nó chỉ có khoảng 5000 dòng mã, nhưng tất cả đều nằm trong đó main
, và tác giả đã sử dụng khoảng 15 hoặc hơn goto
nhánh của cả hai hướng. Đây là mã xấu để bắt đầu, nhưng sự hiện diện của những cái đó goto
làm cho nó tồi tệ hơn nhiều. Đồng nghiệp của tôi mất khoảng 2 tuần để giải quyết vấn đề kiểm soát. Thậm chí tốt hơn, những goto
kết quả đó đã tạo ra mã kết hợp chặt chẽ với chính nó đến mức chúng ta không thể thực hiện bất kỳ thay đổi nào mà không phá vỡ một cái gì đó.
Chúng tôi đã thử biên dịch với tối ưu hóa cấp 1 và trình biên dịch đã ăn hết RAM có sẵn, sau đó tất cả trao đổi có sẵn, rồi hoảng loạn hệ thống (có lẽ không liên quan gì đến goto
bản thân chúng, nhưng tôi thích ném giai thoại đó ra).
Cuối cùng, chúng tôi đã cung cấp cho khách hàng hai tùy chọn - chúng ta hãy viết lại toàn bộ từ đầu hoặc mua phần cứng nhanh hơn.
Họ đã mua phần cứng nhanh hơn.
Quy tắc của Bode khi sử dụng goto
:
- Chi nhánh chỉ chuyển tiếp;
- Không bỏ qua các cấu trúc điều khiển (nghĩa là không phân nhánh vào phần thân của một
if
hoặc for
hoặc while
câu lệnh);
- Không sử dụng
goto
thay cho cấu trúc điều khiển
Có những trường hợp a goto
là câu trả lời đúng, nhưng chúng rất hiếm (thoát ra khỏi một vòng lặp được lồng sâu là về nơi duy nhất tôi sử dụng nó).
CHỈNH SỬA
Mở rộng về tuyên bố cuối cùng đó, đây là một trong số ít trường hợp sử dụng hợp lệ cho goto
. Giả sử chúng ta có chức năng sau:
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
for ( i = 0; i < N; i ++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
for ( j = 0; j < M; j++ )
{
arr[i][j] = malloc( sizeof *arr[i][j] * P );
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
return arr;
}
Bây giờ, chúng ta có một vấn đề - nếu một trong những malloc
cuộc gọi thất bại giữa chừng thì sao? Không chắc là một sự kiện có thể xảy ra, chúng tôi không muốn trả về một mảng được phân bổ một phần, chúng tôi cũng không muốn bảo lãnh ra khỏi hàm có lỗi; chúng tôi muốn dọn dẹp sau khi chính mình và giải phóng bất kỳ bộ nhớ được phân bổ một phần. Trong một ngôn ngữ đưa ra một ngoại lệ đối với phân bổ xấu, điều đó khá đơn giản - bạn chỉ cần viết một trình xử lý ngoại lệ để giải phóng những gì đã được phân bổ.
Trong C, bạn không có cấu trúc xử lý ngoại lệ; bạn phải kiểm tra giá trị trả về của mỗi malloc
cuộc gọi và thực hiện hành động thích hợp.
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( i = 0; i < N; i ++ )
{
if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
goto cleanup_1;
for ( j = 0; j < M; j++ )
{
if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
goto cleanup_2;
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
}
goto done;
cleanup_2:
// We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
while ( j-- )
free( arr[i][j] );
free( arr[i] );
// fall through
cleanup_1:
// We failed while allocating arr[i]; free up all previously allocated arr[i][j]
while ( i-- )
{
for ( j = 0; j < M; j++ )
free( arr[i][j] );
free( arr[i] );
}
free( arr );
arr = NULL;
done:
return arr;
}
Chúng ta có thể làm điều này mà không cần sử dụng goto
? Tất nhiên chúng ta có thể - nó chỉ cần thêm một chút sổ sách kế toán (và, trong thực tế, đó là con đường tôi đi). Nhưng, nếu bạn đang tìm kiếm những nơi sử dụng goto
không phải là ngay lập tức một dấu hiệu của sự thực hành xấu hoặc thiết kế, đây là một trong số ít.