Tại sao sizeof (x ++) không tăng x?


505

Đây là đoạn mã được biên dịch trong dev c ++ windows:

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d and ", sizeof(x++)); // note 1
    printf("%d\n", x); // note 2
    return 0;
}

Tôi dự kiến xlà 6 sau khi thực hiện ghi chú 1 . Tuy nhiên, đầu ra là:

4 and 5

Bất cứ ai có thể giải thích tại sao xkhông tăng sau ghi chú 1 ?


37
Tôi lưu ý rằng DevC ++ sử dụng trình biên dịch lỗi thời, bạn có thể muốn nâng cấp lên IDE mới hơn, ví dụ: Codeblocks Eclipse hoặc Visual Studio
Tom J Nowell 22/11/11

4
++ x tạo ra kết quả tương tự như x ++, cygwin và gcc 3.4.4.
yeh Nam

6
@Tim Pletzcker Bởi vì giá trị đầu tiên là sizeof của biến chứ không phải chính biến đó.
Surfbutler

Câu trả lời của tôi đã được thêm vào do hợp nhất với câu trả lời này, câu trả lời của tôi thực sự cho thấy cách bạn có thể đánh giá thời gian chạy trong trường hợp VLAskhông ai trong số những người khác làm.
Shafik Yaghmour

2
loại văn bản này nên tránh vì nó có thể gây ra tác dụng phụ không mong muốn. Câu hỏi của bạn đã là một trong số họ.
user666412

Câu trả lời:


537

Từ tiêu chuẩn C99 (trọng tâm là của tôi)

6.5.3.4/2

Toán tử sizeof mang lại kích thước (tính bằng byte) của toán hạng của nó, có thể là một biểu thức hoặc tên được ngoặc đơn của một loại. Kích thước được xác định từ loại toán hạng. Kết quả là một số nguyên. Nếu loại toán hạng là loại mảng có chiều dài thay đổi, toán hạng được ước tính; mặt khác, toán hạng không được ước tính và kết quả là hằng số nguyên.


51
"Nếu loại toán hạng là loại mảng có độ dài thay đổi, toán hạng được ước tính" wow! Tôi chưa bao giờ nhận ra điều đó
Kos

6
bạn có ý nghĩa gì bởi loại mảng có chiều dài thay đổi? Điều đó có nghĩa là toán hạng là một mảng? Mã trong trường hợp này không phải là một mảng. Bạn có thể làm rõ mọi thứ cho tôi?
Neigyl R. Noval

37
Mảng có chiều dài thay đổi là một mảng được khai báo với kích thước là một giá trị không xác định trong quá trình biên dịch, ví dụ nếu bạn đọc Ntừ stdin và make int array[N]. Đây là một trong những tính năng của C99, không có trong C ++.
Kos

21
@LegendofCage, đặc biệt điều này có nghĩa là trong một cái gì đó như sizeof(int[++x])(thực sự, thực sự là một ý tưởng tồi, dù sao đi nữa) ++có thể được đánh giá.
Jens Gustyt

3
@Joe Wreschnig: Nó được đánh giá bằng cách gcc, clangvà tại ideone.com/Pf7iF
JFS

190

sizeoflà một toán tử thời gian biên dịch , vì vậy tại thời điểm biên dịch sizeofvà toán hạng của nó được thay thế bằng giá trị kết quả. Các toán hạng là không được đánh giá (trừ khi nó là một mảng chiều dài thay đổi) ở tất cả; chỉ loại kết quả quan trọng.

short func(short x) {  // this function never gets called !!
   printf("%d", x);    // this print never happens
   return x;
}

int main() {
   printf("%d", sizeof(func(3))); // all that matters to sizeof is the 
                                  // return type of the function.
   return 0;
}

Đầu ra:

2

như shortchiếm 2 byte trên máy của tôi.

Thay đổi kiểu trả về của hàm thành double:

double func(short x) {
// rest all same

sẽ đưa ra 8như đầu ra.


12
Chỉ đôi khi - đó là thời gian biên dịch nếu có thể.
Martin Beckett

10
-1, vì điều này mâu thuẫn với câu trả lời bị đánh dấu (và chính xác) và không trích dẫn tiêu chuẩn.
sam hocevar

1
Có một lợi ích tốt khi biên dịch độ phân giải thời gian của toán tử sizeof () khi làm việc với các chuỗi. Nếu bạn có một chuỗi được khởi tạo dưới dạng chuỗi được trích dẫn, thay vì sử dụng strlen (), trong đó mảng ký tự bao gồm chuỗi phải được quét cho bộ kết thúc null trong thời gian chạy, sizeof (quoteed_ chuỗi) được biết đến trong thời gian biên dịch, và do đó tại thời gian chạy. Đó là một điều nhỏ, nhưng nếu bạn sử dụng chuỗi trích dẫn trong một vòng lặp hàng triệu triệu lần, nó sẽ tạo ra sự khác biệt đáng kể về hiệu suất.
dùng2548100

Nếu bạn thực sự sử dụng nó hàng triệu và hàng triệu lần trong một vòng lặp, thì việc tính toán độ dài ra khỏi vòng lặp có hợp lý hơn nhiều không? Tôi chắc chắn hy vọng bạn không có hàng triệu và hàng triệu hằng số mã hóa khác nhau trong mã của mình. : -o
Veky

47

sizeof(foo) rất cố gắng để khám phá kích thước của một biểu thức tại thời điểm biên dịch:

6.5.3.4:

Toán tử sizeof mang lại kích thước (tính bằng byte) của toán hạng của nó, có thể là một biểu thức hoặc tên được ngoặc đơn của một loại. Kích thước được xác định từ loại toán hạng. Kết quả là một số nguyên. Nếu loại toán hạng là loại mảng có chiều dài thay đổi, toán hạng được ước tính; mặt khác, toán hạng không được ước tính và kết quả là hằng số nguyên.

Tóm lại: mảng có chiều dài thay đổi, chạy trong thời gian chạy. (Lưu ý: Mảng độ dài biến là một tính năng cụ thể - không phải là mảng được phân bổ malloc(3).) Mặt khác, chỉ có loại biểu thức được tính toán và tại thời điểm biên dịch.


33

sizeoflà một toán tử dựng sẵn thời gian biên dịch và không phải là một hàm. Điều này trở nên rất rõ ràng trong các trường hợp bạn có thể sử dụng nó mà không cần dấu ngoặc đơn:

(sizeof x)  //this also works

1
Nhưng làm thế nào đây là một câu trả lời cho câu hỏi?
Sebastian Mach

5
@phresnel: Điều này chỉ để làm rõ rằng sizeof là "lạ" và không tuân theo các quy tắc của các chức năng thông thường. Dù sao tôi cũng đã chỉnh sửa bài đăng để loại bỏ sự nhầm lẫn có thể xảy ra với các toán tử thời gian chạy bình thường như (+) và (-)
hugomg

Các sizeofnhà điều hành là không một nhà điều hành thời gian biên dịch, bạn chỉ cần cung cấp cho nó một VLA con số này ra.
paxdiablo

21

Ghi chú

Câu trả lời này đã được hợp nhất từ ​​một bản sao, điều này giải thích ngày muộn.

Nguyên

Ngoại trừ mảng chiều dài thay đổi sizeof không đánh giá các đối số của nó. Chúng ta có thể thấy điều này từ dự thảo tiêu chuẩn C99 Phần 6.5.3.4 toán tử sizeof đoạn 2 có nội dung:

Toán tử sizeof mang lại kích thước (tính bằng byte) của toán hạng của nó, có thể là một biểu thức hoặc tên được ngoặc đơn của một loại. Kích thước được xác định từ loại toán hạng. Kết quả là một số nguyên. Nếu loại toán hạng là loại mảng có chiều dài thay đổi, toán hạng được ước tính; mặt khác, toán hạng không được ước tính và kết quả là hằng số nguyên.

Một nhận xét ( hiện đã bị xóa ) hỏi liệu một cái gì đó như thế này sẽ đánh giá vào thời gian chạy:

sizeof( char[x++]  ) ;

và thực sự nó sẽ, một cái gì đó như thế này cũng sẽ hoạt động ( Xem cả hai sống ):

sizeof( char[func()]  ) ;

vì chúng là cả hai mảng có chiều dài thay đổi. Mặc dù, tôi không thấy sử dụng thực tế trong một trong hai.

Lưu ý, mảng có chiều dài thay đổi được trình bày trong dự thảo phần tiêu chuẩn C99 Bộ 6.7.5.2 khai báo mảng đoạn 4 :

[...] Nếu kích thước là một biểu thức hằng số nguyên và loại phần tử có kích thước không đổi đã biết, loại mảng không phải là kiểu mảng có chiều dài thay đổi; mặt khác, kiểu mảng là kiểu mảng có chiều dài thay đổi.

Cập nhật

Trong C11, câu trả lời thay đổi cho trường hợp VLA, trong một số trường hợp nhất định, không xác định được biểu thức kích thước có được đánh giá hay không. Từ phần 6.7.6.2 khai báo mảng có ghi:

[...] Trong đó biểu thức kích thước là một phần của toán hạng của toán tử sizeof và việc thay đổi giá trị của biểu thức kích thước sẽ không ảnh hưởng đến kết quả của toán tử, không xác định được biểu thức kích thước có được đánh giá hay không.

Ví dụ trong trường hợp như thế này ( xem trực tiếp ):

sizeof( int (*)[x++] )

1
Điều quan trọng cần nhớ ở đây là hầu hết thời gian, thực sự sizeoflà một macro - nó không tạo mã, nhưng tính toán trước giá trị mong đợi và gửi thẳng vào mã. Lưu ý rằng đây là hành vi duy nhất cho đến C99, vì VBA không tồn tại (tôi chưa bao giờ thực sự nghe về chúng cho đến khi có câu trả lời này, tin hay không!)
Corley Brigman

Bằng cách nào sẽ sizeof (char[x++]);sử dụng giá trị của xcho bất cứ điều gì ngoài việc xác định giá trị của biểu thức x++và giá trị mới cho x, cả hai đều bình thường với toán tử đó?
supercat

@alk ... err, yeah, ý tôi là 'VLA' tất nhiên :) Shafik - tại sao những người đó sẽ đánh giá trong thời gian chạy? như tôi đã nói, tôi chưa bao giờ thấy các VLA, nhưng các loại trong số đó đều được biết đến vào thời gian biên dịch, phải không?
Corley Brigman

@CorleyBrigman câu trả lời linh hoạt sẽ là b / c tiêu chuẩn nói như vậy, nhưng lý do là vì chúng tôi không biết kích thước mảng tại thời điểm biên dịch nên chúng tôi phải đánh giá biểu thức trong thời gian chạy. VLAs là một chủ đề thú vị, đây là hai bài viết tôi có trên đâyđây .
Shafik Yaghmour

@Shafik - ahh, ok, tôi đoán sự nhầm lẫn của tôi là tôi đã không nhận ra đó char[x++]là một VLA. nó trông giống như một char*đôi mắt không quen biết của tôi.
Corley Brigman

11

Vì toán hạng của sizeoftoán tử không được đánh giá, bạn có thể làm điều này:

int f(); //no definition, which means we cannot call it

int main(void) {
        printf("%d", sizeof(f()) );  //no linker error
        return 0;
}

Bản demo trực tuyến: http://ideone.com/S8e2Y

Đó là, bạn không cần xác định hàm fnếu nó chỉ được sử dụng sizeof. Kỹ thuật này chủ yếu được sử dụng trong siêu lập trình mẫu C ++, vì ngay cả trong C ++, toán hạng của sizeofkhông được đánh giá.

Tại sao điều này làm việc? Nó hoạt động vì sizeoftoán tử không hoạt động theo giá trị , thay vào đó nó hoạt động theo loại biểu thức. Vì vậy, khi bạn viết sizeof(f()), nó hoạt động trên loại biểu thức f(), và không có gì ngoài kiểu trả về của hàm f. Kiểu trả về luôn giống nhau, bất kể giá trị nào hàm sẽ trả về nếu nó thực sự thực thi.

Trong C ++, bạn thậm chí có thể:

struct A
{
  A(); //no definition, which means we cannot create instance!
  int f(); //no definition, which means we cannot call it
};

int main() {
        std::cout << sizeof(A().f())<< std::endl;
        return 0;
}

Tuy nhiên, có vẻ như, sizeoflần đầu tiên tôi tạo một thể hiện A, bằng cách viết A()và sau đó gọi hàm ftrên cá thể, bằng cách viết A().f(), nhưng không có điều đó xảy ra.

Bản trình diễn: http://ideone.com/egPMi

Đây là một chủ đề khác giải thích một số tính chất thú vị khác của sizeof:


10

Việc thực thi không thể xảy ra trong quá trình biên dịch. Vì vậy ++i/ i++sẽ không xảy ra. Cũng sizeof(foo())sẽ không thực hiện chức năng nhưng trả về đúng loại.


2
" Việc thực thi không thể xảy ra trong quá trình biên dịch. " Ý bạn là gì?
tò mò

1
Quá trình biên dịch sẽ chỉ tạo mã đối tượng ... Mã đối tượng sẽ chỉ được thực thi khi người dùng thực thi nhị phân. Khi sizeof xảy ra tại thời điểm biên dịch, giả sử i ++ sẽ tăng sai.
rakesh

" Khi sizeof xảy ra tại thời gian biên dịch ", ý bạn là: "như sizeoflà một biểu thức hằng số thời gian biên dịch"?
tò mò 23/11/11

Giống như "#define" xảy ra trong quá trình tiền xử lý, kích thước tương tự sẽ xảy ra vào thời gian biên dịch. Trong quá trình biên dịch, tất cả các thông tin loại đều có sẵn để sizeof được đánh giá sau đó và trong quá trình biên dịch và giá trị được thay thế. Như đã được đề cập bởi @pmg "Từ tiêu chuẩn C99" trước đây.
rakesh

1
" sizeof sẽ xảy ra vào thời gian biên dịch " cho một thứ không phải là mảng có độ dài thay đổi
tò mò

0

sizeofchạy ở thời gian biên dịch, nhưng x++chỉ có thể được đánh giá tại thời gian chạy. Để giải quyết điều này, tiêu chuẩn C ++ cho rằng toán hạng của sizeofkhông được đánh giá. Tiêu chuẩn C nói:

Nếu loại toán hạng [của sizeof] là kiểu mảng có độ dài thay đổi, toán hạng được ước tính; mặt khác, toán hạng không được ước tính và kết quả là hằng số nguyên.


Không có VLA trong C ++.
LF

-2

sizeof() Toán tử chỉ cho kích thước của kiểu dữ liệu, nó không đánh giá các phần tử bên trong.


Điều này là sai, sizeof()toán tử hành động đệ quy và sẽ lấy kích thước theo byte của tất cả các phần tử của một container, các thành viên của một lớp hoặc cấu trúc, v.v. Bạn có thể chứng minh điều này với chính mình rất dễ dàng bằng cách tạo một lớp đơn giản với một vài thành viên và kêu gọi sizeof()nó (Tuy nhiên, bất cứ điều gì trong đó là một con trỏ, nó không thể thấy kích thước của - chỉ kích thước của con trỏ.) Điều này xảy ra tất cả tại thời gian biên dịch, như các nhà bình luận khác đã nêu: các biểu thức bên trong sizeof()không được đánh giá.
Tyler Shellberg ngày
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.