2010-10-02 32 views
7

Đây là chức năng của tôi:luận Chức năng chuyền ngược

void abc(char *def, unsigned int w, unsigned int x, unsigned int y, unsigned int z) 
{ 
    printf("val 1 : %d\n", w); 
    printf("val 2 : %d\n", x); 
    printf("val 3 : %d\n", y); 
    printf("val 4 : %d\n", z); 
} 

và đây là nơi mà tôi gọi chức năng này:

unsigned int exp[4] = { 1, 2, 3, 4 }; 
unsigned short count = 0; 
abc(anyarray, exp[count++], exp[count++], exp[count++], exp[count++]); 

và đây là kết quả mà tôi mong đợi:

val1 : 1 
val2 : 2 
val3 : 3 
val4 : 4 

nhưng nội dung tôi nhận được hoàn toàn ngược lại:

val1 : 4 
val2 : 3 
val3 : 2 
val4 : 1 

Tôi không biết tại sao? Bất kỳ trợ giúp sẽ được đánh giá cao.

+0

Printf của bạn cũng bị hỏng, nó sẽ ăn không gian đầu tiên trong chuỗi định dạng! :) –

Trả lời

8

Từ tài liệu tiêu chuẩn, 5,4

Trừ khi có ghi chú, trình tự đánh giá của các toán hạng của các nhà khai thác cá nhân và subexpressions của biểu thức cá nhân, và thứ tự mà tác dụng phụ xảy ra, là unspecified58) Giữa điểm trình tự trước đó và tiếp theo, đối tượng vô hướng sẽ có giá trị được lưu trữ được sửa đổi nhiều nhất một lần bằng cách đánh giá biểu thức. Hơn nữa, giá trị trước đó chỉ được truy cập để xác định giá trị được lưu trữ.Các yêu cầu của đoạn này sẽ được đáp ứng cho mỗi thứ tự cho phép của các biểu thức con của một biểu thức đầy đủ; nếu không hành vi là không xác định.

Một ví dụ từ các tài liệu tiêu chuẩn riêng của mình,

i = v[i ++];// the behavior is undefined

Và đó là vì lý do rất tương tự mà

abc(anyarray, exp[count++], exp[count++], exp[count++], exp[count++]); là undefined ..

-4

Điều này là do quy ước gọi điện. Trong _cdecl, quy ước gọi mặc định cho các chương trình c/C++ (theo microsoft), các tham số được truyền trên stack tới hàm theo thứ tự ngược lại. Bởi vì điều này, các tham số cũng được đánh giá theo thứ tự ngược lại.

+4

Điều đó không hoàn toàn đúng. Không có gì trong tiêu chuẩn nói rằng các đối số được chuyển tới ngăn xếp từ phải sang trái (ví dụ: cách gcc thực hiện). Trong thực tế, không có gì nói rằng có phải là một ngăn xếp ở tất cả. – NullUserException

+0

Tôi đang viết chương trình của mình trên vòm 64 bit. vì vậy tôi giả định ở đây rằng các thông số fucntion sẽ được lưu trữ trong sổ đăng ký. –

+0

Có, nhưng tôi đang nói về quy ước gọi mà trình biên dịch cụ thể này sử dụng, có vẻ là phổ biến nhất. –

2

Bạn đã gọi hành vi không xác định, bằng cách sửa đổi count nhiều lần mà không cần can thiệp sequence point.

4

Bạn không nên sử dụng toán tử ++, hoạt động trên cùng một biến, nhiều lần trong cùng một câu lệnh. Thứ tự mà thao tác sẽ được thực hiện không được xác định.

Hãy thử:

abc(anyarray, exp[count], exp[count+1], exp[count+2], exp[count+3]); 
count += 4; 
+1

'Bạn không nên sử dụng toán tử ++ nhiều lần trong cùng một câu lệnh' Tại sao chúng ta không thể? 'c = a ++ + b ++' được xác định rõ. –

+2

@Prasoon: OK, chỉnh sửa để giải quyết vấn đề này. Tuy nhiên, nhiều sử dụng ưa thích của toán tử tăng dẫn đến loại vấn đề trong câu hỏi ban đầu. Tôi sẽ viết c = a + b; a ++; b ++; bản thân mình – Andrew

1

Bạn đang trông chờ vào các thông số được đánh giá trái sang phải. Bạn không thể đưa ra bất kỳ giả định nào về thứ tự mà chúng được đánh giá. Trong trường hợp này, có vẻ như trình biên dịch đang đánh giá chúng từ phải sang trái.

Ngoài ra, bạn có thể muốn tra cứu các điểm chuỗi, vì có thể bạn không nên sử dụng toán tử ++ theo cách này.

+0

Không có "có thể" về nó. Các tiêu chuẩn tên này rất giống như là ** hành vi undefined **, có nghĩa là trình biên dịch là miễn phí để làm bất cứ điều gì - thậm chí gây ra quỷ mũi. – greyfade

+0

Vâng, nhưng trớ trêu thay nó cũng có nghĩa là nó có thể hoạt động hoàn toàn theo cách bạn mong đợi. Dĩ nhiên, bạn có lẽ là "có thể" là tôi tránh quá quy tắc trong một câu trả lời, mặc dù, trong trường hợp này, một quy tắc nhỏ có lẽ là thích hợp. – jwismar

1

abc(anyarray, exp[count++], exp[count++], exp[count++], exp[count++]);

Trình tự đánh giá của các đối số của abckhông xác định nhưng sự biểu hiện gọi hành vi undefined vì bạn đang cố gắng để sửa đổi một biến count nhiều hơn một lần giữa hai điểm theo thứ tự.

Ngoài ra, việc sử dụng định dạng không đúng định dạng trong printf() cũng sẽ gọi UB. Vui lòng đảm bảo bạn đã sử dụng các thông số định dạng đúng (ví dụ: %u cho unsigned int) trong printf().

+1

Có thể nhận tất cả các giá trị 1 hoặc tất cả các giá trị 4. –

+0

Tôi nghĩ rằng điều quan trọng cần lưu ý là 'đếm' là một biến vô hướng và do đó nó là UB. Đây không phải là vấn đề nếu 'đếm' nếu của UDT – Chubsdad

0

Bạn nhận được điều này bởi vì bạn được gọi là adb(exp,a,b,c,d) theo vấn đề của bạn, nhưng trong khi gọi hàm d được đẩy đầu tiên trên ngăn xếp và sau đó c ,b tương đối. Khi bạn vượt qua exp[count++] tại đối số cuối cùng sẽ xử lý trước tiên để đẩy lên chồng nghĩa 1 được đẩy đầu tiên sau đó 2 rồi 3 rồi 4. Và trong pop chức năng được gọi là thực hiện để bạn có được w=4 x=3 y=2 z=1 đó là nó.

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