Tại sao '&&' mà không phải '&'?


135

Tại sao &&thích hơn &||thích hơn |?

Tôi đã hỏi ai đó đã lập trình trong nhiều năm và lời giải thích của anh ấy là:

Ví dụ, trong if (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1phải đúng khi kiểm tra bool2xem nó phải đúng trước khi chuyển sang bool3, v.v. Nếu tôi đã sử dụng một &thay vì không có thứ tự kiểm tra ngay cả khi tất cả chúng phải đúng tiến tới dòng tiếp theo, vậy tại sao nó lại quan trọng?

Lưu ý: Tôi muốn chỉ ra rằng tôi là người lập trình tương đương với một đứa trẻ mới biết đi và đây không phải là một câu hỏi nghiêm trọng hay cấp bách. Đó là vấn đề hiểu được tại sao mọi thứ nên được thực hiện theo một cách nhất định trái ngược với cách khác.


55
& & & | | và || là các nhà khai thác hoàn toàn khác nhau
Felice Pollano

3
Được thử lại, vì điều này không chỉ áp dụng cho C #
Jonathan Dickinson

12
Hoàn nguyên, vì các câu trả lời đã dành riêng cho C # và hoạt động bên trong có thể khác một chút trong các ngôn ngữ khác có chung khái niệm.
Daniel Hilgarth

8
@Felice: Chúng khác nhau, nhưng hầu như không hoàn toàn khác nhau. Thực tế chúng rất giống nhau: x & yx && ysẽ luôn đánh giá cùng một kết quả nếu x và y là các biểu thức của kiểu boolean. Trong thực tế, sự khác biệt duy nhất trong trường hợp đó dường như là trong x & y, y luôn được đánh giá.
Joren

1
@slawekin: Tôi khuyên bạn nên đọc qua các câu trả lời. Một số viết rộng rãi về sự khác biệt hiệu suất. Câu trả lời có thể làm bạn ngạc nhiên.
Abel

Câu trả lời:


183

Trong hầu hết các trường hợp, &&||được ưu tiên hơn &|vì trước đây được ngắn mạch, có nghĩa là việc đánh giá bị hủy ngay khi kết quả rõ ràng.

Thí dụ:

if(CanExecute() && CanSave())
{
}

Nếu CanExecutetrả về false, biểu thức hoàn chỉnh sẽ là false, bất kể giá trị trả về của CanSave. Bởi vì điều này, CanSavekhông được thực hiện.

Điều này rất tiện dụng trong các trường hợp sau đây:

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValuetrả về falsenếu không tìm thấy khóa được cung cấp trong từ điển. Do tính chất ngắn mạch của &&, value.Contains("test")chỉ được thực hiện, khi TryGetValuetrả về truevà do đó valuethì không null. Nếu bạn sẽ sử dụng toán tử bitwise AND& thay vào đó, bạn sẽ nhận được NullReferenceExceptionnếu không tìm thấy khóa trong từ điển, bởi vì phần thứ hai của biểu thức được thực thi trong mọi trường hợp.

Một ví dụ tương tự nhưng đơn giản hơn về điều này là đoạn mã sau (như được đề cập bởi TJHeuvel):

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecutechỉ được thực hiện nếu opkhông null. Nếu opnull, phần đầu tiên của biểu thức ( op != null) ước tính falsevà phần đánh giá của phần còn lại ( op.CanExecute()) bị bỏ qua.

Ngoài ra, về mặt kỹ thuật, chúng khác nhau, quá:
&&||chỉ có thể được sử dụng trên booltrong khi &|có thể được sử dụng trên bất kỳ loại không thể thiếu ( bool, int, long, sbyte, ...), bởi vì họ là những nhà khai thác Bitwise. &toán tử bitwise AND|toán tử bitwise OR .

Rất chính xác, trong C #, các toán tử đó ( &, |[và ^]) được gọi là "Toán tử logic" (xem thông số kỹ thuật C # , chương 7.11). Có một số triển khai của các toán tử này:

  1. Đối với số nguyên ( int, uint, longulong, chương 7.11.1):
    Họ đang thực hiện để tính toán bitwise kết quả của các toán hạng và các nhà điều hành, tức &là thực hiện để tính toán các phép toán logic AND, vv
  2. Đối với bảng liệt kê (chương 7.11.2):
    Chúng được thực hiện để thực hiện thao tác logic của kiểu liệt kê cơ bản.
  3. Đối với bool và bool null (chương 7.11.3 và 7.11.4):
    Kết quả không được tính bằng cách sử dụng các phép tính bitwise. Kết quả về cơ bản được tìm kiếm dựa trên các giá trị của hai toán hạng, vì số lượng khả năng là rất nhỏ.
    Bởi vì cả hai giá trị được sử dụng để tra cứu, việc triển khai này không bị đoản mạch.

31
Điều này cũng có thể thuận tiện cho việc kiểm tra nếu một cái gì đó là null. Ví dụ : if(op != null && op.CanExecute()). Bởi vì nguyên nhân thứ hai không được đánh giá khi nguyên nhân thứ nhất không đúng, điều này là hợp lệ.
TJHeuvel

2
@TJHeuvel: Đây là cách sử dụng cơ bản giống như tôi đã mô tả với TryGetValueví dụ của mình . Nhưng vâng, đó là một ví dụ tốt về điều này.
Daniel Hilgarth

4
Câu trả lời tốt. Có lẽ bạn cũng nên thêm một ví dụ về cách &hoặc |được sử dụng với các đối số không phải là bool (tức là những gì các toán tử làm) vì lợi ích của tất cả những người mới.
Zabba

81

Để giải thích rất rõ điều này có nghĩa là gì (mặc dù các câu trả lời khác gợi ý về nó - nhưng có thể sử dụng thuật ngữ bạn không hiểu).

Các mã sau đây:

if (a && b)
{
   Foo();
}

Được biên soạn thực sự cho điều này:

if (a)
{
    if (b)
    {
        Foo();
    }
}

Trường hợp đoạn mã sau được biên dịch chính xác như được trình bày:

if (a & b)
{
   Foo();
}

Điều này được gọi là ngắn mạch. Nói chung, bạn nên luôn luôn sử dụng &&||trong điều kiện của bạn.

Điểm thưởng: Có một kịch bản khi bạn không nên. Nếu bạn đang ở trong tình huống mà hiệu suất là rất quan trọng (và đây là mức cực kỳ quan trọng của nano giây ), chỉ sử dụng đoản mạch khi bạn phải (ví dụ: nullkiểm tra) - vì ngắn mạch là một nhánh / nhảy; điều này có thể dẫn đến một sự hiểu lầm chi nhánh trên CPU của bạn; một &là rẻ hơn nhiều so với &&. Ngoài ra còn có một kịch bản trong đó ngắn mạch thực sự có thể phá vỡ logic - hãy xem câu trả lời này của tôi.

Diatribe / Độc thoại : Liên quan đến dự đoán sai nhánh mà hầu hết đều bỏ qua. Trích dẫn Andy Firth (người đã làm việc trên các trò chơi trong 13 năm): "Đây có thể là mức độ thấp hơn mà mọi người nghĩ rằng họ cần phải đi ... nhưng họ đã sai. Hiểu cách phần cứng bạn lập trình để xử lý các chi nhánh có thể ảnh hưởng đến hiệu suất ở mức độ LỚN ... hơn rất nhiều so với hầu hết các lập trình viên có thể đánh giá lại: cái chết bởi hàng ngàn vết cắt. "

  • Các nhà phát triển trò chơi (và những người khác làm việc trong điều kiện thời gian thực khắc nghiệt) đi xa hơn là tái cấu trúc logic của họ để phù hợp hơn với người dự đoán. Cũng có bằng chứng về điều này trong mã mscorlib dịch ngược.
  • Chỉ vì .NET bảo vệ bạn khỏi loại điều này không có nghĩa là nó không quan trọng. Một dự đoán sai chi nhánh đắt khủng khiếp ở 60 Hz; hoặc ở mức 10.000 yêu cầu / giây.
  • Intel sẽ không có công cụ để xác định vị trí của các dự đoán sai, Windows cũng sẽ không có bộ đếm hiệu suất cho việc này, cũng như không có từ nào để mô tả về nó, vì đó không phải là vấn đề.
  • Sự thiếu hiểu biết về các cấp độ thấp hơn và kiến ​​trúc không làm cho ai đó nhận thức được chúng sai.
  • Luôn cố gắng để hiểu những hạn chế của phần cứng bạn đang làm việc.

Đây là một điểm chuẩn cho những người không tin. Tốt nhất là chạy quy trình trong RealTime / High để giảm thiểu lịch trình có hiệu lực: https://gist.github.com/1200737


7
Về "điểm thưởng": Tất cả chúng ta đều biết những điều tốt đẹp đến từ việc tối ưu hóa sớm. :)
một CVn

6
@Michael - đó là lý do tại sao 'nano giây quan trọng' được in đậm :). Các nhà phát triển trò chơi AAA thường lo lắng về những thứ như thế này - và bạn không bao giờ biết ai sẽ đọc câu trả lời; Vì vậy, luôn luôn tốt nhất để ghi lại ngay cả các trường hợp biên giới / cực đoan.
Jonathan Dickinson

1
Điểm thưởng đó có hợp lệ cho C # không? Tôi đã nghĩ là không, vì MSIL được diễn giải, trừ khi biểu thức được biên dịch ngay xuống mã máy.
Jeremy McGee

7
@Jeremy MSIL không được giải thích.
Jonathan Dickinson

2
@TheD kiểm tra lại câu trả lời - Tôi đã thêm một đoạn độc thoại về lý do tại sao bạn NÊN lo lắng về điều này. Và, FYI, (x && y)dịch sang LOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;nơi (x & y)dịch sang LOAD x; LOAD y; AND; BRANCH_FALSE;. Một nhánh so với hai.
Jonathan Dickinson

68

Toán tử logic ( ||&&) so với toán tử bitwise ( |& ).

Sự khác biệt quan trọng nhất giữa toán tử logic và toán tử bitwise là toán tử logic lấy hai booleans và tạo boolean trong khi toán tử bitwise lấy hai số nguyên và tạo ra một số nguyên (lưu ý: số nguyên có nghĩa là bất kỳ loại dữ liệu tích phân nào, không chỉ int).

Để được mô tả, một toán tử bitwise lấy một mẫu bit (ví dụ: 01101011) và thực hiện AND / OR một chút trên mỗi bit. Vì vậy, ví dụ nếu bạn có hai số nguyên 8 bit:

a     = 00110010 (in decimal:    32+16+2   = 50)
b     = 01010011 (in decimal: 64+   16+2+1 = 83)
----------------
a & b = 00010010 (in decimal:       16+2   = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

trong khi một toán tử logic chỉ hoạt động trong bool:

a      = true
b      = false
--------------
a && b = false
a || b = true

Thứ hai, thường có thể sử dụng toán tử bitwise trên bool vì true và false tương ứng với 1 và 0, và điều đó xảy ra là nếu bạn dịch true thành 1 và false thành 0, sau đó thực hiện thao tác bitwise, sau đó chuyển đổi khác không đúng và không thành sai; nó xảy ra rằng kết quả sẽ giống như bạn đã sử dụng toán tử logic (kiểm tra điều này để thực hiện).

Một điểm khác biệt quan trọng nữa là toán tử logic bị ngắn mạch . Do đó, trong một số vòng kết nối [1], bạn thường thấy mọi người làm điều gì đó như thế này:

if (person && person.punch()) {
    person.doVictoryDance()
}

có nghĩa là: "nếu con người tồn tại (tức là không phải là null), hãy cố gắng đấm anh ấy / cô ấy và nếu cú ​​đấm thành công (tức là trở về đúng), thì hãy thực hiện một điệu nhảy chiến thắng" .

Nếu bạn đã sử dụng một toán tử bitwise thay thế, điều này:

if (person & person.punch()) {
    person.doVictoryDance()
}

sẽ dịch thành: "nếu người tồn tại (tức là không rỗng) và cú đấm thành công (tức là trả về đúng), thì hãy thực hiện một điệu nhảy chiến thắng" .

Lưu ý rằng trong toán tử logic ngắn mạch, person.punch()mã có thể không được chạy nếu không personcó giá trị. Trong thực tế, trong trường hợp cụ thể này, mã thứ hai sẽ tạo ra lỗi tham chiếu null nếu personlà null, vì nó cố gắng gọi person.punch()bất kể người đó có null hay không. Hành vi không đánh giá toán hạng đúng này được gọi là ngắn mạch .

[1] Một số lập trình viên sẽ bực bội vì đã thực hiện một cuộc gọi hàm có tác dụng phụ bên trong một if biểu thức, trong khi đối với những người khác, đó là một thành ngữ phổ biến và rất hữu ích.

Vì toán tử bitwise hoạt động trên 32 bit mỗi lần (nếu bạn đang sử dụng máy 32 bit), nó có thể dẫn đến mã thanh lịch hơn và nhanh hơn nếu bạn cần so sánh một số lượng lớn các điều kiện, ví dụ:

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
    CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;

Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;

Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;

Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;

CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;

// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

Làm tương tự với các toán tử logic sẽ đòi hỏi một lượng so sánh khó xử:

Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;

Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;

Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;

CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch         = bar.rules.can_punch         && person.can_punch,
     cloc1.usable_abilities.can_kick          = bar.rules.can_kick          && person.can_kick,
     cloc1.usable_abilities.can_drink         = bar.rules.can_drink         && person.can_drink,
     cloc1.usable_abilities.can_sit           = bar.rules.can_sit           && person.can_sit,
     cloc1.usable_abilities.can_shoot_guns    = bar.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
     cloc1.usable_abilities.can_talk          = bar.rules.can_talk          && person.can_talk;

bool cloc2.usable_abilities.can_punch         = military.rules.can_punch         && person.can_punch,
     cloc2.usable_abilities.can_kick          = military.rules.can_kick          && person.can_kick,
     cloc2.usable_abilities.can_drink         = military.rules.can_drink         && person.can_drink,
     cloc2.usable_abilities.can_sit           = military.rules.can_sit           && person.can_sit,
     cloc2.usable_abilities.can_shoot_guns    = military.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc2.usable_abilities.can_talk          = military.rules.can_talk          && person.can_talk,
     cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

Một ví dụ cổ điển trong đó các mẫu bit và toán tử bitwise được sử dụng là trong các quyền của hệ thống tệp Unix / Linux.


3
+1 cho bên ảnh hưởng đến vấn đề. Đáng ngạc nhiên là nó đã không được đề cập trước đó
Conrad Frix

3
ví dụ này có vẻ hơi bạo lực, nhưng có vẻ như các câu trả lời khác tập trung quá nhiều vào việc đoản mạch và không đủ về sự khác biệt giữa hoạt động trên số nguyên và trên booleans.
R0MANARMY

Chức năng phải được hiểu trước khi chi tiết thực hiện (ngắn mạch / tác dụng phụ) đi vào hoạt động. Vui mừng bạn đã xóa sự khác biệt chính là logic boolean so với logic số nguyên, không phải là ngắn mạch.
Abel

@ROMANARMY - bạo lực, tôi thích sự trớ trêu đưa ra cho nhà sư của bạn. Làm tốt lắm
brumScouse

8

Trong trường hợp:

if (obj != null && obj.Property == true) { }

sẽ làm việc như mong đợi.

Nhưng:

if (obj != null & obj.Property == true) { }

có khả năng có thể ném một ngoại lệ tham chiếu null.


2

Ngắn gọn và đơn giản:

1 && 2= true

1 = true (khác không) trong C
2 = true (khác không) trong C

trueANDS hợp lý với trueđể cung cấp true.

Nhưng

1 & 2= 0 = false

1 = 0001 trong nhị phân
2 = 0010 trong nhị phân

0001 ANDs bitwise với 0010 để cho 0000 = 0 ở dạng thập phân.

Tương tự như vậy đối với | | và | khai thác quá ...!


2
-1: Chúng ta đang nói về C # ở đây ... 1 && 2là bất hợp pháp trong C #
Daniel Hilgarth

Nhưng đây là một ví dụ cực kỳ quan trọng giải thích lý do tại sao bạn không thể đơn giản trao đổi & & & (mà nhiều người dường như nghĩ).
bobobobo

1

&& là phiên bản ngắn mạch của & .

Nếu chúng ta đang đánh giá false & true, chúng ta đã biết từ việc nhìn vào đối số đầu tiên rằng kết quả sẽ là sai. Các &&phiên bản của các nhà điều hành sẽ trả lại kết quả trong thời gian sớm nhất có thể, chứ không phải là đánh giá toàn bộ biểu thức. Ngoài ra còn có một verion tương tự của các |nhà điều hành ||,.


1
if (list.Count() > 14 && list[14] == "foo")

an toàn

if (list.Count() > 14 & list[14] == "foo")

sẽ sụp đổ nếu danh sách không có kích thước phù hợp.


Tôi không thể tưởng tượng ai đó có thể viết "if (list.Count ()> 14 & list [14] ==" foo ")" thay vì "if (list.Count ()> 14 && list [14] ==" foo ")". & chỉ đơn giản và tự nhiên không thể được sử dụng cho && trong trường hợp này ngay cả khi & chắc chắn là an toàn (ví dụ: danh sách [1]).
Tiến Đỗ

1

Toán tử C # nên giải thích tại sao:

Về cơ bản có hai &hoặc hai |nghĩa là nó là một điều kiện chứ không phải là logic, vì vậy bạn có thể nói sự khác biệt giữa hai.

& Toán tử có một ví dụ về việc sử dụng một&.


Cả hai liên kết đều (bị hỏng) một cách hiệu quả (chuyển hướng đến "Tài liệu đã nghỉ hưu của Visual Studio 2005" ).
Peter Mortensen

1

OK, trên mệnh giá

    Boolean a = true;
    Boolean b = false;

    Console.WriteLine("a({0}) && b({1}) =  {2}", a, b, a && b);
    Console.WriteLine("a({0}) || b({1}) =  {2}", a, b, a || b);
    Console.WriteLine("a({0}) == b({1}) =  {2}", a, b, a == b);

    Console.WriteLine("a({0}) & b({1}) =  {2}", a, b, a & b);
    Console.WriteLine("a({0}) | b({1}) =  {2}", a, b, a | b);
    Console.WriteLine("a({0}) = b({1}) =  {2}", a, b, a = b);

đưa ra câu trả lời tương tự. Tuy nhiên, như bạn đã chỉ ra, nếu bạn có một câu hỏi phức tạp hơn thì:

if (a and b and c and d) ..

Nếu điều đó akhông đúng và có thể blà một chức năng mà nó phải tắt, kết nối với thứ gì đó, lấy cái này, làm cái kia, đưa ra quyết định .. tại sao phải bận tâm? Lãng phí thời gian, bạn biết nó đã thất bại. Tại sao làm cho máy tắt và làm thêm công việc vô nghĩa?

Tôi đã luôn sử dụng &&bởi vì tôi đặt nhiều khả năng thất bại đầu tiên, ergo, ít tính toán trước khi tiếp tục khi không có điểm. Nếu không có cách nào để dự đoán các lựa chọn ít khả năng hơn, chẳng hạn như bạn có boolean để giới hạn đầu ra của dữ liệu, đại loại như:

if (limit && !MyDictionary.ContainsKey("name")) 
    continue;

Nếu không limit, đừng bận tâm kiểm tra chìa khóa, việc này có thể mất nhiều thời gian hơn ..


1

Khi được sử dụng trong một biểu thức logic như câu lệnh if &&thích hợp hơn vì nó sẽ dừng đánh giá các biểu thức ngay khi gặp kết quả sai đầu tiên. Điều này là có thể bởi vì một giá trị sai sẽ làm cho toàn bộ biểu thức là sai. Tương tự (và một lần nữa trong các biểu thức logic)|| là tốt hơn bởi vì nó sẽ dừng đánh giá các biểu thức ngay khi nó gặp một biểu thức đúng bởi vì bất kỳ giá trị thực nào cũng sẽ làm cho toàn bộ biểu thức là đúng.

Tuy nhiên, nếu các biểu thức là hay-ed hay và-ed cùng có tác dụng phụ, và bạn muốn tất cả những xảy ra như là kết quả của biểu thức của bạn (không phụ thuộc vào kết quả của biểu thức logic), sau đó &|có thể được sử dụng. Ngược lại, các toán tử &&||toán tử có thể hữu ích như các trình bảo vệ chống lại các tác dụng phụ không mong muốn (chẳng hạn như một con trỏ null gây ra một ngoại lệ được ném ra).

Các toán tử &|cũng có thể được sử dụng với các số nguyên và trong trường hợp này chúng tạo ra một kết quả số nguyên là hai toán hạng và-ed hoặc-ed với nhau ở cấp độ bit. Điều này có thể hữu ích khi các bit nhị phân của một giá trị nguyên được sử dụng như một mảng các giá trị đúng và sai. Để kiểm tra xem một bit nhất định được bật hay tắt, mặt nạ bit được bit bit và ed với giá trị. Để bật một chút, cùng một mặt nạ có thể là bitwise hoặc ed với giá trị. Cuối cùng để tắt một chút, phần bù bit (sử dụng ~) của mặt nạ là bitwise và phù hợp với giá trị.

int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
    // will do something
}
a = a & (~4) // turn bit off again, a is now 000

Trong các ngôn ngữ khác ngoài C #, phải cẩn thận với các chế độ logic và bitwise của & và |. Trong đoạn mã trên, ifbiểu thức điều kiện của câu lệnh (a & 4) != 0là một cách an toàn để biểu thị điều kiện này, nhưng trong nhiều ngôn ngữ giống như C, các câu lệnh có điều kiện có thể chỉ đơn giản coi các giá trị nguyên bằng 0 là giá trị nguyên sai và không bằng 0 là đúng. (Lý do cho điều này liên quan đến các hướng dẫn của bộ xử lý nhánh có điều kiện và mối quan hệ của chúng với cờ zero được cập nhật sau mỗi hoạt động số nguyên.) Vì vậy, ìfkiểm tra câu lệnh cho số 0 có thể được gỡ bỏ và điều kiện có thể được rút ngắn (a & 4).

Điều này có thể gây nhầm lẫn và thậm chí có thể có vấn đề khi các biểu thức được kết hợp bằng cách sử dụng các giá trị bitwise và toán tử trả về các bit không có các bit xếp hàng. Xem xét ví dụ sau trong đó các hiệu ứng phụ của hai hàm được mong muốn, trước khi kiểm tra xem cả hai đều thành công (như được xác định bởi chúng trả về giá trị khác không):

if (foo() & bar()) {
    // do something
}

Trong C, nếu foo()trả về 1 và bar()trả về 2, "cái gì đó" sẽ không được thực hiện vì 1 & 2bằng không.

C # yêu cầu các câu lệnh có điều kiện muốn ifcó một oeprand boolean và ngôn ngữ không cho phép một giá trị nguyên được chuyển thành giá trị boolean. Vì vậy, đoạn mã trên sẽ tạo ra lỗi trình biên dịch. Nó sẽ được trình bày chính xác hơn như sau:

if (foo() != 0 & bar() != 0) {
    // do something
}

1

Nếu bạn là một lập trình viên C hẹn giờ cũ, hãy cẩn thận . C # đã thực sự làm tôi ngạc nhiên.

MSDN nói cho |toán tử:

Nhị phân | toán tử được xác định trước cho các loại tích phân và bool . Đối với các loại tích phân, | tính toán bitwise OR của toán hạng của nó. Đối với toán hạng bool, | tính toán logic OR của toán hạng của nó; đó là, kết quả là sai khi và chỉ khi cả hai toán hạng của nó là sai.

(Nhấn mạnh là của tôi.) Các loại Boolean được xử lý đặc biệt và trong bối cảnh này, câu hỏi chỉ bắt đầu có ý nghĩa, và sự khác biệt là, như những câu trả lời khác đã được đưa ra trong câu trả lời của họ:

&&||bị đoản mạch. &|đánh giá cả hai toán hạng.

và những gì tốt hơn phụ thuộc vào nhiều thứ như hiệu ứng phụ, hiệu suất và khả năng đọc mã, nhưng nhìn chung các toán tử ngắn mạch cũng thích hợp hơn bởi vì những người có nền tảng tương tự như tôi hiểu rõ hơn.

Lý do là: Tôi sẽ lập luận như thế này: Vì không có loại boolean thực trong C, bạn có thể sử dụng toán tử bitwise |và có kết quả của nó được đánh giá là trung thực hoặc giả trong điều kiện if. Nhưng đây là thái độ sai đối với C #, vì đã có trường hợp đặc biệt cho các loại boolean.


0

Điều này rất quan trọng, vì nếu chi phí đánh giá bool2 (ví dụ) cao nhưng bool1 là sai, thì bạn đã tiết kiệm cho mình một chút tính toán hợp lý bằng cách sử dụng && over &


0

Bởi vì &&||được sử dụng để kiểm soát dòng chảy giống như if/elselà. Nó không phải luôn luôn là về điều kiện. Hoàn toàn hợp lý khi viết như một tuyên bố, không phải là một ifhoặc một whileđiều kiện, như sau:

 a() && b() && c() && d();

hoặc thậm chí

 w() || x() || y() || z();

Không chỉ là những thứ đó dễ gõ hơn các if/elsephiên bản tương đương ; chúng cũng dễ đọc và dễ hiểu hơn nhiều.


0

&& và & có nghĩa là hai điều rất khác nhau và cung cấp cho bạn hai câu trả lời khác nhau.

1 && 2mang lại 1 ("đúng")
1 & 2mang lại 0 ("sai")

&&là một toán tử logic - nó có nghĩa là "đúng nếu cả hai toán hạng đều đúng"
&là so sánh theo bit. Nó có nghĩa là "cho tôi biết bit nào được đặt trong cả hai toán hạng"


2
Câu hỏi là về C #. Trong C #, không có cách nào để chuyển một số thành bool, vì vậy 0 không phải là 'false' và nonzero không phải là 'true'; đơn giản là không có sự tương đương
Nate CK

Để chuyển đổi số thành bool, theo cách 1 có nghĩa là đúng và 0 có nghĩa là sai, hãy nói "n! = 0" (Tôi giả sử ... tôi không thực sự quen thuộc với C #). Trên thực tế tôi muốn rút lại nhận xét này vì nó không được nghiên cứu kỹ và tôi không nghĩ nó hữu ích hay thực sự phù hợp với nhận xét trước đây mà tôi nghĩ nhiều hơn về nó, nhưng tôi vô tình nhấn enter và bây giờ tôi không nghĩ mình có thể hủy bỏ nó để bạn đi, vì bất cứ điều gì nó có giá trị :-)
Don hatch

1 && 2đưa ra lỗi trình biên dịch: "Lỗi 4 Toán tử '&&' không thể được áp dụng cho các toán hạng loại 'int' và 'int'"
Peter Mortensen

0

Cách nhanh nhất (và hơi ngớ ngẩn) để giải thích điều này cho những người không CẦN phải biết các hoạt động chính xác của mã khi thực hiện việc này là

&& đang kiểm tra từng điều kiện cho đến khi tìm thấy sai và trả về toàn bộ kết quả là sai

| | đang kiểm tra từng điều kiện cho đến khi tìm thấy đúng và trả về toàn bộ kết quả là đúng.

& đang thực hiện MATHS dựa trên apon CẢ / TẤT CẢ các điều kiện và xử lý kết quả.

| đang thực hiện MATHS dựa trên apon CẢ / TẤT CẢ các điều kiện và xử lý kết quả.

Tôi chưa bao giờ bắt gặp một điểm mà tôi cần sử dụng & hoặc | trong một câu lệnh if. Tôi chủ yếu sử dụng nó để cắt các giá trị thập lục phân thành các màu thành phần của nó bằng cách sử dụng dịch chuyển bit.

VÍ DỤ:

r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;

Trong hoạt động này "& 0xFF" buộc phải chỉ nhìn vào giá trị nhị phân. Cá nhân tôi chưa tìm thấy cách sử dụng cho | mặc dù


0

Đơn giản,

if exp1 && exp2

nếu exp1 flase không kiểm tra exp2

nhưng

if exp1 & exp2

nếu exp1 là falseHoặc true kiểm tra exp2

và hiếm khi mọi người sử dụng &vì họ hiếm khi muốn kiểm tra exp2 nếu exp1 làfalse

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.