Trong C và C ++, phương pháp nào có thể ngăn chặn việc sử dụng ngẫu nhiên phép gán (=) khi cần tương đương (==)?


15

Trong C và C ++, rất dễ viết mã sau đây với một lỗi nghiêm trọng.

char responseChar = getchar();
int confirmExit = 'y' == tolower(responseChar);
if (confirmExit = 1)
{
    exit(0);
}

Lỗi là câu lệnh if nên có:

if (confirmExit == 1)

Như được mã hóa, nó sẽ thoát ra mỗi lần, bởi vì việc gán confirmExitbiến xảy ra, sau đó confirmExitđược sử dụng làm kết quả của biểu thức.

Có những cách tốt để ngăn chặn loại lỗi này?


39
Đúng. Bật cảnh báo trình biên dịch. Coi cảnh báo là lỗi và nó không phải là vấn đề
Martin York


9
Trong ví dụ đã cho, giải pháp rất dễ. Bạn gán cho nó một giá trị boolean, do đó sử dụng nó làm giá trị boolean : if (confirmExit).
Bảo mật

4
Vấn đề là lỗi do các "nhà thiết kế" của ngôn ngữ C gây ra, khi họ chọn sử dụng = cho toán tử gán và == để so sánh bằng. ALGOL đã sử dụng: =, vì họ đặc biệt muốn sử dụng = để so sánh bình đẳng, và PASCAL và Ada tuân theo quyết định ALGOL. . phần mềm. "Một người mong muốn rằng DoD và các nhà thầu đã lắng nghe Bell Labs về điều này.)
John R. Strohm

4
@ John, sự lựa chọn của các biểu tượng không phải là vấn đề, thực tế là các bài tập cũng là một biểu thức trả về giá trị được gán, cho phép a = bhoặc a == btrong một điều kiện.
Karl Bielefeldt

Câu trả lời:


60

Kỹ thuật tốt nhất là tăng mức cảnh báo của trình biên dịch của bạn. Sau đó, nó sẽ cảnh báo bạn về nhiệm vụ tiềm năng trong nếu có điều kiện.

Hãy chắc chắn rằng bạn biên dịch mã của mình với các cảnh báo bằng không (dù sao bạn cũng nên thực hiện). Nếu bạn muốn trở thành pedantic thì hãy đặt trình biên dịch của bạn để coi các cảnh báo là lỗi.

Sử dụng điều kiện Yoda (đặt hằng số ở phía bên tay trái) là một kỹ thuật phổ biến khoảng một thập kỷ trước. Nhưng chúng làm cho mã khó đọc hơn (và do đó duy trì do cách đọc không tự nhiên (trừ khi bạn là Yoda)) và không mang lại lợi ích nào lớn hơn việc tăng mức cảnh báo (cũng có thêm lợi ích của nhiều cảnh báo hơn).

Cảnh báo là các lỗi logic thực sự trong mã và cần được sửa chữa.


2
Chắc chắn đi với các cảnh báo, không phải là điều kiện Yoda - bạn có thể quên thực hiện hằng số điều kiện trước tiên dễ dàng như bạn có thể quên làm ==.
Michael Kohne

8
Tôi có một bất ngờ thú vị rằng câu trả lời được bình chọn nhiều nhất cho câu hỏi này là 'biến cảnh báo của trình biên dịch thành lỗi'. Tôi đã mong đợi sự if (0 == ret)kinh hoàng.
James

2
@James: ... chưa kể nó sẽ không hoạt động a == b!!
Emilio Garavaglia

6
@EmilioGaravaglia: Có một cách giải quyết dễ dàng cho điều đó: Chỉ cần viết 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(lặp lại cho tất cả các giá trị có thể). Đừng quên ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||lỗi ... bắt buộc , hoặc các lập trình viên bảo trì sẽ không có bất kỳ niềm vui nào.
user281377

10

Bạn luôn có thể làm một cái gì đó triệt để như kiểm tra phần mềm của bạn. Tôi thậm chí không có nghĩa là các thử nghiệm đơn vị tự động, chỉ là các thử nghiệm mà mỗi nhà phát triển có kinh nghiệm duy nhất không có thói quen bằng cách chạy mã mới của mình hai lần, một lần xác nhận thoát và một lần không. Đó là lý do hầu hết các lập trình viên coi đó là một vấn đề.


1
Dễ dàng thực hiện khi hành vi của chương trình của bạn là hoàn toàn xác định.
James

3
Vấn đề đặc biệt này có thể khó kiểm tra. Tôi đã thấy những người bị cắn rc=MethodThatRarelyFails(); if(rc = SUCCESS){nhiều lần, đặc biệt là nếu phương pháp này chỉ thất bại trong điều kiện khó kiểm tra.
Gort Robot

3
@StevenBurnap: Đó là những gì các đối tượng giả dành cho. Kiểm tra thích hợp bao gồm kiểm tra các chế độ thất bại.
Jan Hudec

Đây là câu trả lời chính xác.
Cuộc đua nhẹ nhàng với Monica

6

Một cách truyền thống để ngăn chặn việc sử dụng các bài tập trong biểu thức không chính xác là đặt hằng số ở bên trái và biến ở bên phải.

if (confirmExit = 1)  // Unsafe

if (1=confirmExit)    // Safe and detected at compile time.

Trình biên dịch sẽ báo cáo lỗi cho việc gán bất hợp pháp thành hằng số tương tự như sau.

.\confirmExit\main.cpp:15: error: C2106: '=' : left operand must be l-value

Việc sửa đổi nếu điều kiện sẽ là:

  if (1==confirmExit)    

Như thể hiện bởi các bình luận dưới đây, điều này được nhiều người coi là một phương pháp không phù hợp.



13
Cần lưu ý rằng làm mọi thứ theo cách này sẽ khiến bạn khá không phổ biến.
riwalk

11
Xin đừng đề nghị thực hành này.
James

5
Nó cũng không hoạt động nếu bạn cần so sánh hai biến với nhau.
dan04

Một số ngôn ngữ thực sự cho phép gán 1 cho một giá trị mới (vâng, tôi biết Q là một cơn
sốt

4

Tôi đồng ý với tất cả mọi người nói "cảnh báo trình biên dịch" nhưng tôi muốn thêm một kỹ thuật khác: Đánh giá mã. Nếu bạn có chính sách xem xét tất cả các mã được cam kết, tốt nhất là trước khi cam kết, thì có khả năng loại điều này sẽ bị bắt trong khi xem xét.


Tôi không đồng ý. Đặc biệt = thay vì == có thể dễ dàng lướt qua bằng cách xem lại mã.
Simon

2

Đầu tiên, nâng mức cảnh báo của bạn không bao giờ làm tổn thương.

Nếu bạn không muốn điều kiện của mình kiểm tra kết quả của một phép gán trong chính câu lệnh if, thì đã làm việc với rất nhiều lập trình viên C và C ++ trong nhiều năm qua và chưa bao giờ nghe rằng so sánh hằng số đầu tiên if(1 == val)là một điều xấu, bạn có thể thử xây dựng đó.

Nếu người lãnh đạo dự án của bạn chấp thuận việc bạn làm điều này, đừng lo lắng về những gì người khác nghĩ. Bằng chứng thực sự là liệu bạn hoặc người khác có thể hiểu được mã của bạn hàng tháng và hàng năm kể từ bây giờ.

Tuy nhiên, nếu bạn có ý định kiểm tra kết quả của một nhiệm vụ, thì việc sử dụng các cảnh báo cao hơn có thể [có thể đã] bắt được nhiệm vụ đó thành một hằng số.


Tôi nhướn mày hoài nghi ở bất kỳ lập trình viên mới bắt đầu nào thấy Yoda có điều kiện và không hiểu ngay lập tức. Nó chỉ là một cú lật, không khó đọc và chắc chắn không tệ như một số bình luận đang tuyên bố.
gạch dưới

@underscore_d Tại hầu hết các nhà tuyển dụng của tôi, các bài tập trong một điều kiện đã được tán thành. Suy nghĩ là tốt hơn để tách nhiệm vụ ra khỏi điều kiện. Lý do rõ ràng hơn, thậm chí với chi phí của một dòng mã khác là yếu tố kỹ thuật bền vững. Tôi đã làm việc ở những nơi có cơ sở mã lớn và rất nhiều bảo trì bị tồn đọng. Tôi hoàn toàn hiểu ai đó có thể muốn gán giá trị và chi nhánh với điều kiện phát sinh từ việc chuyển nhượng. Tôi thấy nó được thực hiện thường xuyên hơn trong Perl, và, nếu ý định rõ ràng, tôi sẽ theo mô hình thiết kế của tác giả.
bạch tuộc

Tôi đã suy nghĩ về Yoda có điều kiện một mình (như bản demo của bạn ở đây), không phải với sự phân công (như trong OP). Tôi không bận tâm cái trước nhưng cũng không giống cái sau. Hình thức duy nhất tôi cố tình sử dụng là if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {vì nó tránh được việc vô dụng nullptrtrong phạm vi nếu diễn viên thất bại - đó có lẽ là lý do C ++ có khả năng hạn chế này trong một điều kiện. Đối với phần còn lại, vâng, một định nghĩa sẽ có dòng riêng, tôi nói - dễ nhìn hơn và ít bị đánh lừa hơn.
gạch dưới

@underscore_d Câu trả lời được chỉnh sửa dựa trên ý kiến ​​của bạn. Điểm tốt.
bạch tuộc

1

Đến bữa tiệc muộn hơn bao giờ hết, nhưng Phân tích mã tĩnh là chìa khóa ở đây

Hầu hết các IDE hiện cung cấp SCA trên và trên kiểm tra cú pháp của trình biên dịch và các công cụ khác có sẵn, bao gồm cả các công cụ thực hiện các hướng dẫn MISRA (*) và / hoặc CERT-C.

Tuyên bố: Tôi là một phần của nhóm làm việc MISRA C, nhưng tôi đang đăng trong khả năng cá nhân. Tôi cũng độc lập với bất kỳ nhà cung cấp công cụ nào


-2

Chỉ cần sử dụng gán tay trái, các cảnh báo của trình biên dịch có thể giúp ích nhưng bạn phải chắc chắn rằng mình đạt được cấp độ phù hợp nếu không bạn sẽ bị ngập trong các cảnh báo vô nghĩa hoặc sẽ không được nói với những gì bạn muốn xem.


4
Bạn sẽ ngạc nhiên về số lượng cảnh báo vô nghĩa mà trình biên dịch hiện đại tạo ra. Bạn có thể không nghĩ rằng một cảnh báo là quan trọng, nhưng trong hầu hết các trường hợp, bạn chỉ sai.
Kristof Provost

Kiểm tra cuốn sách, "Viết mã rắn" amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/ ,. Nó bắt đầu với một cuộc thảo luận tuyệt vời về trình biên dịch và chúng ta có thể nhận được bao nhiêu lợi ích từ các thông điệp cảnh báo và phân tích tĩnh sâu rộng hơn.
Nhà phát

@KristofProvost Tôi đã quản lý để có được phòng thu trực quan đưa cho tôi 10 bản sao của cùng một cảnh báo cho cùng một dòng mã, sau đó khi vấn đề này được 'khắc phục', nó đã dẫn đến 10 cảnh báo giống hệt nhau về cùng một dòng mã nói rằng phương pháp ban đầu đã tốt hơn.
Đảo ngược Llama
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.