Toán tử dấu phẩy hoạt động như thế nào


175

Toán tử dấu phẩy hoạt động như thế nào trong C ++?

Chẳng hạn, nếu tôi làm:

a = b, c;  

Có một kết thúc bằng b hoặc c?

(Vâng, tôi biết điều này rất dễ kiểm tra - chỉ cần ghi lại ở đây để ai đó tìm thấy câu trả lời nhanh chóng.)

Cập nhật: Câu hỏi này đã bộc lộ một sắc thái khi sử dụng toán tử dấu phẩy. Chỉ để tài liệu này:

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

Câu hỏi này thực sự được lấy cảm hứng từ một lỗi đánh máy trong mã. Những gì đã được dự định là

a = b;
c = d;

Đã trở thành

a = b,    //  <-  Note comma typo!
c = d;


1
Bản sao có thể có của toán tử dấu phẩy `,` làm gì trong C? . Nó đánh bại bạn một ngày. Và câu trả lời của lillq cung cấp một câu trả lời cho câu hỏi về a = (b, c);.
jww

5
Nhưng trong trường hợp này a = b, c = d;thực sự không thực hiện như dự định a = b; c = d;?
Bondolin

@NargothBond Không nhất thiết. Nếu bdlà các đánh giá chức năng sử dụng (và sửa đổi) một trạng thái chung, thứ tự thực hiện không được xác định cho đến khi C++17.
nyronium

Câu trả lời:



129

Hãy lưu ý rằng toán tử dấu phẩy có thể bị quá tải trong C ++. Do đó, hành vi thực tế có thể rất khác so với dự kiến.

Ví dụ, Boost.Sprite sử dụng toán tử dấu phẩy khá khéo léo để triển khai các trình khởi tạo danh sách cho các bảng ký hiệu. Vì vậy, nó làm cho cú pháp sau có thể và có ý nghĩa:

keywords = "and", "or", "not", "xor";

Lưu ý rằng do quyền ưu tiên của toán tử, mã này (cố ý!) Giống hệt với

(((keywords = "and"), "or"), "not"), "xor";

Đó là, toán tử đầu tiên được gọi là keywords.operator =("and")trả về một đối tượng proxy mà các operator,s còn lại được gọi:

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");

Umm, bạn không thể thay đổi quyền ưu tiên, có nghĩa là bạn có thể nên đặt dấu ngoặc đơn xung quanh danh sách của mình.
Jeff Burdges

18
@Jeff Ngược lại. Với dấu ngoặc đơn xung quanh danh sách, điều này sẽ không hoạt động kể từ đó trình biên dịch chỉ nhìn thấy toán tử dấu phẩy giữa hai char[], không thể bị quá tải. Mã đầu tiên cố ý gọi operator=và sau đó operator,cho từng phần tử còn lại.
Konrad Rudolph

125

Toán tử dấu phẩy có mức ưu tiên thấp nhất trong tất cả các toán tử C / C ++. Do đó, nó luôn là cái cuối cùng liên kết với một biểu thức, có nghĩa là:

a = b, c;

tương đương với:

(a = b), c;

Một sự thật thú vị khác là toán tử dấu phẩy giới thiệu một điểm chuỗi . Điều này có nghĩa là biểu thức:

a+b, c(), d

được đảm bảo có ba biểu thức con ( a + b , c ()d ) được đánh giá theo thứ tự. Điều này rất có ý nghĩa nếu chúng có tác dụng phụ. Thông thường các trình biên dịch được phép đánh giá các biểu thức con theo bất kỳ thứ tự nào chúng thấy phù hợp; ví dụ, trong một lệnh gọi hàm:

someFunc(arg1, arg2, arg3)

đối số có thể được đánh giá theo thứ tự tùy ý. Lưu ý rằng dấu phẩy trong lệnh gọi hàm không phải là toán tử; chúng là những dải phân cách.


15
Giá trị chỉ ra rằng ,có độ ưu tiên thấp như vậy, nó thậm chí còn tụt hậu so với bản thân ;) ... Đó là: bằng dấu phẩy như- điều hành có độ ưu tiên thấp hơn so với dấu phẩy-như- tách . Vì vậy, nếu bạn muốn sử dụng toán tử dấu phẩy trong một đối số hàm duy nhất, gán biến hoặc danh sách được phân tách bằng dấu phẩy khác - thì bạn cần sử dụng dấu ngoặc đơn, ví dụ:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

Toán tử dấu phẩy:

  • có quyền ưu tiên thấp nhất
  • là liên kết trái

Một phiên bản mặc định của toán tử dấu phẩy được xác định cho tất cả các loại (tích hợp và tùy chỉnh) và nó hoạt động như sau - được cung cấp exprA , exprB:

  • exprA được đánh giá
  • kết quả exprAbị bỏ qua
  • exprB được đánh giá
  • kết quả của exprBđược trả về là kết quả của toàn bộ biểu thức

Với hầu hết các toán tử, trình biên dịch được phép chọn thứ tự thực hiện và thậm chí còn được yêu cầu bỏ qua việc thực hiện bất kỳ điều gì nếu nó không ảnh hưởng đến kết quả cuối cùng (ví dụ: false && foo()sẽ bỏ qua lệnh gọi đến foo). Tuy nhiên, đây không phải là trường hợp của toán tử dấu phẩy và các bước trên sẽ luôn xảy ra * .

Trong thực tế, toán tử dấu phẩy mặc định hoạt động gần giống như dấu chấm phẩy. Sự khác biệt là hai biểu thức được phân tách bằng dấu chấm phẩy tạo thành hai câu lệnh riêng biệt, trong khi phân tách dấu phẩy giữ tất cả dưới dạng một biểu thức. Đây là lý do tại sao toán tử dấu phẩy đôi khi được sử dụng trong các trường hợp sau:

  • Cú pháp C yêu cầu một biểu thức , không phải là một câu lệnh. ví dụ như trongif( HERE )
  • Cú pháp C yêu cầu một câu lệnh, không nhiều hơn, ví dụ như trong quá trình khởi tạo forvòng lặpfor ( HERE ; ; )
  • Khi bạn muốn bỏ qua các dấu ngoặc nhọn và giữ một câu lệnh: if (foo) HERE ;(xin đừng làm vậy, nó thực sự xấu xí!)

Khi một câu lệnh không phải là một biểu thức, dấu chấm phẩy không thể được thay thế bằng dấu phẩy. Ví dụ: những thứ này không được phép:

  • (foo, if (foo) bar)( ifkhông phải là một biểu thức)
  • int x, int y (khai báo biến không phải là biểu thức)

Trong trường hợp của bạn, chúng tôi có:

  • a=b, c;, tương đương với a=b; c;, giả sử rằng đó alà loại không quá tải toán tử dấu phẩy.
  • a = b, c = d;tương đương với a=b; c=d;, giả sử rằng đó alà loại không quá tải toán tử dấu phẩy.

Xin lưu ý rằng không phải mọi dấu phẩy thực sự là một toán tử dấu phẩy. Một số dấu phẩy có ý nghĩa hoàn toàn khác nhau:

  • int a, b; --- danh sách khai báo biến được phân tách bằng dấu phẩy, nhưng đây không phải là toán tử dấu phẩy
  • int a=5, b=3; --- đây cũng là danh sách khai báo biến được phân tách bằng dấu phẩy
  • foo(x,y)--- danh sách đối số được phân tách bằng dấu phẩy. Trong thực tế, xycó thể được đánh giá theo thứ tự bất kỳ !
  • FOO(x,y) --- danh sách đối số macro được phân tách bằng dấu phẩy
  • foo<a,b> --- danh sách đối số mẫu được phân tách bằng dấu phẩy
  • int foo(int a, int b) --- danh sách tham số được phân tách bằng dấu phẩy
  • Foo::Foo() : a(5), b(3) {} --- danh sách trình khởi tạo được phân tách bằng dấu phẩy trong hàm tạo của lớp

* Điều này không hoàn toàn đúng nếu bạn áp dụng tối ưu hóa. Nếu trình biên dịch nhận ra rằng một đoạn mã nhất định hoàn toàn không có tác động đến phần còn lại, nó sẽ loại bỏ các câu lệnh không cần thiết.

Đọc thêm: http://en.wikipedia.org/wiki/Comma_operator


Có đáng lưu ý rằng nếu operator ,quá tải, bạn sẽ mất bất kỳ sự đảm bảo nào về tính kết hợp (giống như bạn mất các thuộc tính ngắn mạch của operator&&operator||nếu chúng bị quá tải)?
YoungJohn

Toán tử dấu phẩy là liên kết trái bất kể nó có bị quá tải hay không. Một biểu thức a, b, cluôn có nghĩa (a, b), cvà không bao giờ a, (b, c). Việc giải thích sau này thậm chí có thể dẫn đến lỗi biên dịch nếu các phần tử thuộc các loại khác nhau. Bạn có thể theo thứ tự đánh giá các đối số là gì? Tôi không chắc về điều đó, nhưng có lẽ bạn đã đúng: nó có thể xảy ra cđược đánh giá trước (a, b) cả khi dấu phẩy là liên kết trái.
CygnusX1

1
Chỉ cần một nhận xét nhỏ về danh sách khởi tạo được phân tách bằng dấu phẩy trong hàm tạo của lớp, thứ tự không được xác định theo vị trí trong danh sách. Thứ tự được xác định bởi vị trí khai báo của lớp. Ví dụ như di struct Foo { Foo() : a(5), b(3) {} int b; int a; }tản b(3)trước a(5). Điều này rất quan trọng nếu danh sách của bạn giống như vậy : Foo() : a(5), b(a) {}. b sẽ không được đặt thành 5, mà thay vào đó là giá trị chưa được khởi tạo của a, mà trình biên dịch của bạn có thể hoặc không thể cảnh báo.
Jonathan Gawrych

Gần đây tôi đã bắt gặp một toán tử dấu phẩy với hai số float, điểm đánh giá và loại bỏ một số là gì?
Aaron Franke

Tôi không nghĩ ai có thể trả lời điều đó. Bạn sẽ phải thể hiện nó trong một bối cảnh. Có lẽ là một câu hỏi riêng biệt?
CygnusX1

38

Giá trị của asẽ là b, nhưng giá trị của biểu thức sẽ là c. Đó là, trong

d = (a = b, c);

a sẽ bằng b, và dsẽ bằng c.


19
Gần đúng. Báo cáo không có giá trị, biểu thức làm. Giá trị của biểu thức đó là c.
Leon Timmermans

Tại sao điều này được sử dụng thay vì a = b; d = c;?
Aaron Franke

Điều này khiến tôi hiểu những tác dụng phụ mà mọi người đang nói đến.
dây giày

8

giá trị của b sẽ được gán cho a. Sẽ không có gì xảy ra với c


2

Giá trị của a sẽ bằng b, vì toán tử dấu phẩy có độ ưu tiên thấp hơn toán tử gán.


2

Có Toán tử dấu phẩy có độ ưu tiên thấp hơn toán tử Assign

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

Đầu ra: i = 3
Vì toán tử dấu phẩy luôn trả về giá trị ngoài cùng bên phải.
Trong trường hợp toán tử dấu phẩy với Toán tử gán:

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

Ouput: i = 1
Như chúng ta đã biết toán tử dấu phẩy có độ ưu tiên thấp hơn so với gán .....


Vậy ví dụ thứ hai khác với việc chỉ có i = 1;trên dòng đó như thế nào?
Aaron Franke

-3

Điều đầu tiên trước tiên: Dấu phẩy thực sự không phải là một toán tử, đối với trình biên dịch, nó chỉ là một mã thông báo có ý nghĩa trong ngữ cảnh với các mã thông báo khác.

Điều này có nghĩa là gì và tại sao phải bận tâm?

Ví dụ 1:

Để hiểu sự khác biệt giữa ý nghĩa của cùng một mã thông báo trong một bối cảnh khác, chúng ta hãy xem ví dụ này:

class Example {
   Foo<int, char*> ContentA;
}

Thường là C ++ mới bắt đầu sẽ nghĩ rằng biểu hiện này có thể / sẽ so sánh mọi thứ nhưng nó là absolutly sai, ý nghĩa của <, >,mã thông báo depent trên bối cảnh sử dụng.

Tất nhiên, việc giải thích chính xác của ví dụ trên là một bản mẫu của mẫu.

Ví dụ 2:

Khi chúng ta viết một vòng lặp điển hình cho nhiều hơn một biến khởi tạo và / hoặc nhiều hơn một biểu thức nên được thực hiện sau mỗi lần lặp của vòng lặp, chúng ta cũng sử dụng dấu phẩy:

for(a=5,b=0;a<42;a++,b--)
   ...

Ý nghĩa của dấu phẩy phụ thuộc vào bối cảnh sử dụng, ở đây là bối cảnh của việc forxây dựng.

Dấu phẩy trong ngữ cảnh thực sự có nghĩa là gì?

Để làm phức tạp nó hơn nữa (như mọi khi trong C ++), toán tử dấu phẩy có thể bị quá tải (nhờ Konrad Rudolph chỉ ra điều đó).

Để trở lại câu hỏi, Bộ luật

a = b, c;

có nghĩa là cho trình biên dịch một cái gì đó như

(a = b), c;

bởi vì mức độ ưu tiên của =mã thông báo / toán tử cao hơn mức độ ưu tiên của ,mã thông báo.

và điều này được giải thích trong bối cảnh như

a = b;
c;

(lưu ý rằng việc giải thích phụ thuộc vào ngữ cảnh, ở đây, nó không phải là một lời gọi hàm / phương thức hoặc một sự xúi giục mẫu.)


1
chắc chắn là như vậy, có lẽ tôi đã sử dụng thuật ngữ sai (đối với từ vựng đó là mã thông báo, chắc chắn)
Quonux

2
Là một người làm việc với toán tử, (sic), dấu phẩy thực sự là một toán tử.
DragonLord ngày 11 tháng

2
Trong khi nhận ra nếu mã thông báo dấu phẩy được cung cấp được công nhận là toán tử dấu phẩy (ví dụ, ngược lại, phân tách các đối số) có thể là một thách thức riêng, câu hỏi này đặc biệt về toán tử dấu phẩy .
CygnusX1
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.