2010-02-05 37 views
12

Xét đoạn mã sau:sizeof hành xử bất ngờ

#include <stdio.h> 
    int main(void) 
    { 
    int a[10]; 
    printf("%d",(int)sizeof(a)); //prints 10*sizeof(int) (40 on my compiler) 
    printf("%d",(int)sizeof(a-3)); //prints sizeof(int) (4 on my compiler) 

    } 

Tôi biết rằng sizeof() là một nhà điều hành thời gian biên dịch nhưng tôi rất ngạc nhiên khi nhìn thấy đầu ra của thứ hai printf(). Điều gì có thể là lý do? Có một chuyển đổi tiềm ẩn của đối số của sizeof() từ một loại mảng thành một loại số nguyên không?

+3

Thực tế thứ hai in 'sizeof (int *)', không phải 'sizeof (int)'. 'sizeof (int *)' chỉ xảy ra là 4 trên nền tảng của bạn. – AnT

+0

Ngoài ra, cách thích hợp để in một giá trị 'size_t' (giá trị được trả về bởi' sizeof') là với '"% zu "', giả sử trình biên dịch của bạn hỗ trợ tính năng C99 cụ thể đó. Nếu không, đặt cược tốt hơn sẽ là đưa nó vào một 'unsigned' hoặc' unsigned long'. –

Trả lời

30

Toán tử sizeof không đánh giá đối số của nó, chỉ looks at the type toán hạng của nó.

Giả sử bạn có một mảng a với loại "mảng [N] loại T". Sau đó, trong hầu hết các trường hợp, loại tên a là "con trỏ đến T" (T *) và giá trị của con trỏ là địa chỉ của phần tử đầu tiên của mảng (&a[0]). Tức là, tên của một mảng "phân rã" thành con trỏ đến phần tử đầu tiên của nó. Các "mục nát" không xảy ra trong các trường hợp sau đây:

  • khi a được sử dụng với địa chỉ-of (&) điều hành,
  • trong việc khởi tạo a (nó là bất hợp pháp để gán cho mảng trong C) và
  • khi a là toán hạng của toán tử sizeof.

Vì vậy, sizeof a cung cấp cho bạn N lần sizeof(T).

Khi bạn làm sizeof(a-3), loại toán hạng thành sizeof được xác định theo biểu thức a-3. Kể từ a trong a-3 được sử dụng trong một value context (tức là, không có ba ngữ cảnh nào ở trên), loại của nó là "con trỏ đến int" và tên a phân rã thành con trỏ thành a[0]. Như vậy, tính toán a-3 là hành vi không xác định, nhưng vì sizeof không đánh giá đối số của nó, a-3 chỉ được sử dụng để xác định loại toán hạng, vì vậy mã là OK (xem liên kết đầu tiên ở trên để biết thêm).

Từ trên, sizeof(a-3) tương đương với sizeof(int *), là 4 trên máy tính của bạn.

"Chuyển đổi" là do toán tử trừ. Bạn có thể thấy một tương tự, và có lẽ đáng ngạc nhiên hơn, cho kết quả với toán tử dấu phẩy:

printf("%zu\n", sizeof(1, a)); 

cũng sẽ in sizeof(int *), vì các nhà điều hành dấu phẩy dẫn đến a làm quen trong một bối cảnh giá trị.

+0

+1 Rõ ràng hơn tôi nhiều. –

5

(a-3) có loại int* và in bạn sizeof(int*) là 4 trên nền tảng của bạn.

Và lưu ý rằng sizeof() không còn là thời gian biên dịch trong C99 (do mảng có độ dài variadic).

+6

Để rõ ràng - kết quả của 'sizeof' không phải là một thời gian biên dịch liên tục chỉ khi toán hạng là một mảng chiều dài biến. Nó vẫn là một hằng số cho các kiểu toán hạng khác. –

1

Không, trong trường hợp thứ hai, đối số được hiểu là con trỏ int* xảy ra với kích thước bằng 4 trên máy của bạn.

1

sizeof() trả về kích thước của một loại, vì vậy loại này là quan trọng.

Nó cũng không được in với %d. Ít nhất, hãy chuyển nó một cách rõ ràng đến unsigned long hoặc unsigned long long và sử dụng trình định dạng định dạng thích hợp. Khi giảng dạy C, tôi đã có một học sinh nhận được câu trả lời sai bằng cách in size_t với %d như sách giáo khoa nhầm lẫn nói để làm.

Dù sao, a là loại mảng. Trong C, các kiểu mảng phân rã thành kiểu con trỏ nếu bạn làm gần như bất cứ điều gì với chúng hoặc hắt hơi to, vì vậy hầu hết mọi thứ bạn làm với a sẽ mang lại một kiểu con trỏ. Như bạn đã phát hiện, việc cộng hoặc trừ một số sẽ bị phân rã. (Sau khi tất cả, một mảng không thể được sử dụng trong số học, nhưng một con trỏ có thể.)

+0

@David: miễn là kết quả không tràn, nếu một biểu thức biểu hiện dưới dạng '(int)', thì in nó bằng '"% d "' là không sao. Trong trường hợp này, nó gần như chắc chắn rằng kích thước sẽ vừa với một 'int'. Trong C99, người ta sẽ sử dụng '"% zu "' tất nhiên. Đối với C89, đề xuất của bạn tốt hơn việc in với '"% d "' sau khi truyền dưới dạng '(int)' vì nó dễ bị tràn hơn. –

+0

@ Alok: Đúng vậy, nhưng làm việc truyền là quan trọng hơn nhiều so với những gì bạn truyền tới. Ngoài ra, tôi rất quen thuộc với C89, và đã không sử dụng C99, và nó có thể cho thấy. –

+0

OP có dàn diễn viên trong câu hỏi của anh ta. Tôi đồng ý với bạn - không có diễn viên, '"% d "' sai, nhưng lệnh gọi 'printf()' của OP là OK miễn là không có tràn. –