2010-05-28 31 views
6

Đoạn code dưới đây cho thấy sự khác biệt này:Tính ưu tiên của toán tử trong C++ có khác nhau đối với con trỏ và vòng lặp không?

#include <iostream> 
#include <string> 

int main() 
{ 
     char s[] = "ABCD"; 
     std::string str(s); 

     char *p = s; 
     while(*p) { 
       *p++ = tolower(*p);   // <-- incr after assignment 
     } 
     std::cout << s << std::endl; 

     std::string::iterator it = str.begin(), end = str.end(); 
     while(it != end) { 
       *it++ = tolower(*it);  // <-- incr before assignment ? 
     } 
     std::cout << str << std::endl; 

     return 0; 
} 

nó tạo ra:

abcd 
bcd 

nếu chúng ta tách hoạt động phân công và điều hành increment:

while(it != end) { 
    *it = tolower(*it);  // <-- incr before assignment ? 
    it++; 
} 

đầu ra sẽ được như mong đợi.

Có gì sai với mã gốc?

$ g++ --version 
g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) 
Copyright (C) 2004 Free Software Foundation, Inc. 

Trả lời

9

Vấn đề là thứ tự đánh giá đối số của operator= không được chỉ định. Đây là tiêu chuẩn C++ 5.2.2/8. Hãy xem xét những điều sau đây:

*it++ = tolower(*it); 

bằng

operator=(*it++, tolower(*it)); 

Bây giờ *it++ có thể được tính toán trước khi tolower(*it) và ngược lại.

+1

Tệ hơn hành vi không xác định, có hành vi không xác định trong biểu thức gốc. Có thể sắp xếp các biểu thức con khi 'it ++' được đánh giá (tức là ghi vào' nó') trước khi 'nó' được đọc trong việc đánh giá tham số cho' tolower' mà không có điểm chuỗi xen kẽ. Điều này có thể xảy ra cho dù toán tử gán có phải là một cuộc gọi hàm hay không. Dù bằng cách nào 5 [expr], đoạn 8 xác nhận rằng đây là hành vi không xác định. –

2
*it++ = tolower(*it); 
*p++ = tolower(*p); 

Cả hai dòng gọi hành vi không xác định. Bạn không thể sửa đổi giá trị của một biến nhiều hơn một lần trong một câu lệnh đơn (++ sửa đổi một lần, toán tử = sửa đổi hai lần).

Vì vậy, thực tế là bạn nhận được các giá trị khác nhau là không đáng ngạc nhiên.

+0

'toán tử ++' và 'toán tử =' được áp dụng ở đây cho hai biến khác nhau trong mỗi trường hợp. Không có UB. –

+0

@Kirill V. Lyadvinsky: Có hành vi không xác định cho cả trường hợp con trỏ và vòng lặp vì có thể sắp xếp chuỗi hoạt động không có điểm chuỗi xen kẽ giữa giá trị đọc của 'it' (hoặc' p') đối với toán tử '*' trên RHS của phép gán và phép toán gia tăng trên LHS của phép gán. –

+0

Dù trường hợp nào, các dòng chắc chắn gây nhầm lẫn và tôi sẽ không muốn thấy nó được viết như thế :-) –

2

Ngữ pháp hoạt động chính xác như nhau đối với con trỏ và trình lặp. Các thao tác được ngụ ý bởi các toán tử được chuyển thành các cuộc gọi hàm cho các đối tượng kiểu lớp (chẳng hạn như hầu hết các trình vòng lặp).

Vấn đề với mã của bạn không phải là ưu tiên của nhà điều hành, trong cả hai dòng này không có trình tự giữa hoạt động gia tăng và lần đọc thứ hai của cùng một biến được tăng lên ở đâu đó trong câu lệnh. Bởi vì điều này, bạn có hành vi không xác định để anh ta có thể thấy bất kỳ hành vi nào từ chương trình của bạn bao gồm các kết quả mà bạn đang thấy.

*p++ = tolower(*p); 

*it++ = tolower(*it); 

Bạn cần phải định nghĩa lại tuyên bố này theo cách xác định trình tự. Tôi đoán rằng bạn muốn một cái gì đó như thế này.

char c = tolower(*p); 
*p++ = c; 
+0

+1: addon của bạn hoạt động, nhưng tôi đã mơ hồ undestanding lý do tại sao –

+0

Lý do cho downvote, bất cứ ai? –

+0

+1 để có đề xuất tốt. Có thể đó là gợi ý sai cuối cùng, nhưng nó rất hữu ích. Xem xét thêm các liên kết vào Tiêu chuẩn. –

Các vấn đề liên quan