Mengapa sizeof (x ++) tidak bertambah x?

505

Berikut adalah kode yang dikompilasi di 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;
}

Saya berharap xmenjadi 6 setelah mengeksekusi catatan 1 . Namun, hasilnya adalah:

4 and 5

Adakah yang bisa menjelaskan mengapa xtidak naik setelah catatan 1 ?

Neigyl R. Noval
sumber
37
Saya perhatikan bahwa DevC ++ menggunakan kompiler yang sangat lama, Anda mungkin ingin meningkatkan ke IDE yang lebih baru, misalnya Codeblocks Eclipse atau Visual Studio
Tom J Nowell
4
++ x menghasilkan hasil yang sama dengan x ++, cygwin dan gcc 3.4.4.
yehnan
6
@ Tim Pletzcker Karena nilai pertama adalah ukuran dari variabel, dan bukan variabel itu sendiri.
Surfbutler
Jawaban saya ditambahkan karena bergabung dengan yang ini. Jawaban saya sebenarnya menunjukkan bagaimana Anda bisa mendapatkan evaluasi run-time dalam hal VLAsyang tidak ada yang lain lakukan.
Shafik Yaghmour
2
penulisan jenis ini harus dihindari karena dapat menyebabkan efek samping yang tidak diinginkan. Pertanyaan Anda sudah salah satunya.
user666412

Jawaban:

537

Dari Standar C99 (penekanannya adalah milik saya)

6.5.3.4/2

Ukuran operator menghasilkan ukuran (dalam byte) dari operandnya, yang dapat berupa ekspresi atau nama yang dikurung dalam suatu tipe. Ukurannya ditentukan dari jenis operan. Hasilnya adalah bilangan bulat. Jika jenis operan adalah jenis array panjang variabel, operan dievaluasi; jika tidak, operan tidak dievaluasi dan hasilnya adalah konstanta integer.

pmg
sumber
51
"Jika tipe operan adalah tipe array panjang variabel, operan dievaluasi" wow! Saya tidak pernah menyadari bahwa
Kos
6
apa yang Anda maksud dengan tipe array panjang variabel? Itu artinya operan adalah array? Kode dalam hal ini bukan array. Bisakah Anda menjernihkan semuanya untuk saya?
Neigyl R. Noval
37
Array panjang variabel adalah array yang dideklarasikan dengan ukuran menjadi nilai yang tidak diketahui selama kompilasi, misalnya jika Anda membaca Ndari stdin dan membuat int array[N]. Ini adalah salah satu fitur C99, tidak tersedia di C ++.
Kos
21
@ LegegofCage, khususnya ini akan berarti bahwa dalam sesuatu seperti sizeof(int[++x])(benar-benar ide yang buruk, bagaimanapun) ++dapat dievaluasi.
Jens Gustedt
3
@ Jo Wreschnig: Ini dievaluasi oleh gcc, clangdan di ideone.com/Pf7iF
jfs
190

sizeofadalah operator waktu kompilasi , jadi pada saat kompilasi sizeofdan operasinya digantikan oleh nilai hasil. The operan tidak dievaluasi (kecuali bila itu adalah panjang variabel array) sama sekali; hanya jenis hasilnya yang penting.

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;
}

Keluaran:

2

sebagai shortmenempati 2 byte pada mesin saya.

Mengubah jenis fungsi yang dikembalikan ke double:

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

akan memberikan 8sebagai output.

codaddict
sumber
12
Hanya kadang-kadang - waktu kompilasi jika memungkinkan.
Martin Beckett
10
-1, karena ini bertentangan dengan jawaban yang diterima (dan benar), dan tidak mengutip standar.
sam hocevar
1
Ada manfaat yang bagus untuk resolusi waktu kompilasi dari sizeof () operator ketika bekerja dengan string. Jika Anda memiliki string yang diinisialisasi sebagai string yang dikutip, alih-alih menggunakan strlen (), di mana array karakter yang terdiri dari string harus dipindai untuk null-terminator saat run time, sizeof (dikutip_string) dikenal pada waktu kompilasi, dan karena itu pada saat run-time. Ini hal kecil, tetapi jika Anda menggunakan string yang dikutip dalam satu lingkaran jutaan dan jutaan kali, itu membuat perbedaan yang signifikan dalam kinerja.
user2548100
Jika Anda benar-benar menggunakannya jutaan kali dalam satu lingkaran, bukankah akan lebih masuk akal untuk memperhitungkan perhitungan panjang dari loop? Saya sungguh berharap Anda tidak memiliki jutaan dan jutaan konstanta hardcod yang berbeda dalam kode Anda. : -o
Veky
47

sizeof(foo) berusaha sangat keras untuk menemukan ukuran ekspresi pada waktu kompilasi:

6.5.3.4:

Ukuran operator menghasilkan ukuran (dalam byte) dari operandnya, yang dapat berupa ekspresi atau nama yang dikurung dalam suatu tipe. Ukurannya ditentukan dari jenis operan. Hasilnya adalah bilangan bulat. Jika jenis operan adalah jenis array panjang variabel, operan dievaluasi; jika tidak, operan tidak dievaluasi dan hasilnya adalah konstanta integer.

Singkatnya: array panjang variabel, jalankan saat runtime. (Catatan: Array Panjang Variabel adalah fitur khusus - bukan array yang dialokasikan dengan malloc(3).) Jika tidak, hanya jenis ekspresi yang dihitung, dan pada waktu kompilasi.

sarnold
sumber
33

sizeofadalah operator built-time kompilasi dan bukan fungsi. Ini menjadi sangat jelas dalam kasus Anda dapat menggunakannya tanpa tanda kurung:

(sizeof x)  //this also works
hugomg
sumber
1
Tetapi bagaimana ini merupakan jawaban untuk pertanyaan itu?
Sebastian Mach
5
@phrnelnel: Ini hanya untuk memperjelas bahwa sizeof "aneh" dan tidak tunduk pada aturan fungsi normal. Saya tetap mengedit posting untuk menghapus kemungkinan kebingungan dengan operator runtime normal seperti (+) dan (-)
hugomg
The sizeofoperator adalah bukan operator saat kompilasi, Anda hanya perlu memberikan VLA untuk memikirkan hal ini.
paxdiablo
21

Catatan

Jawaban ini digabungkan dari duplikat, yang menjelaskan tanggal terakhir.

Asli

Kecuali untuk array panjang variabel, sizeof tidak mengevaluasi argumennya. Kita bisa melihat ini dari draft bagian standar C99 6.5.3.4 Ukuran operator paragraf 2 yang mengatakan:

Ukuran operator menghasilkan ukuran (dalam byte) dari operandnya, yang dapat berupa ekspresi atau nama yang dikurung dalam suatu tipe. Ukurannya ditentukan dari jenis operan. Hasilnya adalah bilangan bulat. Jika jenis operan adalah jenis array panjang variabel, operan dievaluasi; jika tidak, operan tidak dievaluasi dan hasilnya adalah konstanta integer.

Sebuah komentar ( sekarang dihapus ) bertanya apakah sesuatu seperti ini akan dievaluasi saat berjalan:

sizeof( char[x++]  ) ;

dan memang itu akan terjadi, sesuatu seperti ini juga akan berhasil ( Lihat mereka berdua hidup ):

sizeof( char[func()]  ) ;

karena keduanya adalah array panjang variabel. Meskipun, saya tidak melihat banyak kegunaan praktis di salah satu.

Catatan, array panjang variabel dicakup dalam rancangan bagian standar C99 Deklarator 6.7.5.2 array paragraf 4 :

[...] Jika ukurannya adalah ekspresi konstanta integer dan tipe elemen memiliki ukuran konstan yang diketahui, tipe array bukanlah tipe array panjang variabel; jika tidak, tipe array adalah tipe array panjang variabel.

Memperbarui

Di C11 jawaban berubah untuk kasus VLA, dalam kasus-kasus tertentu tidak ditentukan apakah ekspresi ukuran dievaluasi atau tidak. Dari bagian 6.7.6.2 deklarasi Array yang berbunyi:

[...] Jika ekspresi ukuran adalah bagian dari operan ukuran operator dan mengubah nilai ekspresi ukuran tidak akan mempengaruhi hasil operator, itu tidak ditentukan apakah ekspresi ukuran dievaluasi atau tidak.

Misalnya dalam kasus seperti ini ( lihat langsung ):

sizeof( int (*)[x++] )
Shafik Yaghmour
sumber
1
Yang penting untuk diingat di sini adalah bahwa sebagian besar waktu, sizeofsecara efektif adalah makro - ia tidak membuat kode, tetapi mendahului nilai yang diharapkan dan menyimpannya langsung ke dalam kode. Perhatikan bahwa ini adalah satu - satunya perilaku sampai C99, karena VBA tidak ada (saya tidak pernah benar-benar mendengarnya sampai jawaban ini, percaya atau tidak!)
Corley Brigman
Dengan cara apa akan sizeof (char[x++]);menggunakan nilai xuntuk apa pun selain menentukan nilai ekspresi x++dan nilai baru untuk x, yang keduanya normal dengan operator itu?
supercat
@alk ... err, yeah, maksudku 'VLA' tentu saja :) Shafik - mengapa mereka mengevaluasi saat runtime? seperti yang saya katakan, saya belum pernah melihat VLA, tetapi tipe-tipe itu sama-sama dikenal pada waktu kompilasi, bukan?
Corley Brigman
@CorleyBrigman jawaban cepat akan b / c standar mengatakan demikian, tetapi alasannya adalah b / c kita tidak tahu ukuran array pada waktu kompilasi jadi kita harus mengevaluasi ekspresi saat run-time. VLA adalah topik yang menarik, di sini ada dua posting yang saya miliki di sini dan di sini .
Shafik Yaghmour
@Shafik - ahh, ok, saya kira kebingungan saya adalah saya tidak menyadari char[x++]adalah VLA. kelihatannya seperti char*mata saya yang tidak kenal.
Corley Brigman
11

Karena operan sizeofoperator tidak dievaluasi, Anda dapat melakukan ini:

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

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

Demo online: http://ideone.com/S8e2Y

Artinya, Anda tidak perlu mendefinisikan fungsi fjika hanya digunakan sizeof. Teknik ini banyak digunakan dalam metaprogramming template C ++, karena bahkan dalam C ++, operan sizeoftidak dievaluasi.

Mengapa ini bekerja? Ini bekerja karena sizeofoperator tidak beroperasi pada nilai , melainkan beroperasi pada jenis ekspresi. Jadi ketika Anda menulis sizeof(f()), ini beroperasi pada tipe ekspresi f(), dan yang tidak lain adalah tipe kembalinya fungsi f. Jenis kembali selalu sama, tidak peduli berapa nilai fungsi akan kembali jika itu benar-benar dijalankan.

Di C ++, Anda dapat meratakan ini:

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;
}

Namun sepertinya, dalam sizeof, saya pertama kali membuat contoh A, dengan menulis A(), dan kemudian memanggil fungsif pada contoh, dengan menulis A().f(), tetapi tidak ada hal seperti itu terjadi.

Demo: http://ideone.com/egPMi

Berikut adalah topik lain yang menjelaskan beberapa sifat menarik lainnya dari sizeof:

Nawaz
sumber
10

Eksekusi tidak dapat terjadi selama kompilasi. Jadi ++i/ i++tidak akan terjadi. Juga sizeof(foo())tidak akan menjalankan fungsi tetapi mengembalikan tipe yang benar.

rakesh
sumber
2
" Eksekusi tidak dapat terjadi selama kompilasi. " Apa maksudmu?
curiousguy
1
Kompilasi hanya akan membuat kode objek ... Kode objek hanya akan dijalankan ketika pengguna mengeksekusi biner. Karena sizeof terjadi pada waktu kompilasi dengan asumsi kenaikan i ++ akan salah.
rakesh
" Ketika sizeof terjadi pada waktu kompilasi, " maksud Anda: "seperti halnya sizeofkonstanta waktu kompilasi"?
curiousguy
Seperti "#define" yang terjadi selama pra-pemrosesan, ukuran yang sama akan terjadi pada waktu kompilasi. Selama kompilasi semua informasi jenis tersedia sehingga sizeof dievaluasi saat itu juga selama kompilasi dan nilai diganti. Seperti yang sudah disebutkan oleh @pmg "Dari Standar C99" sebelumnya.
rakesh
1
" sizeof akan terjadi pada waktu kompilasi " untuk sesuatu yang bukan array panjang variabel
curiousguy
0

sizeofberjalan pada waktu kompilasi, tetapi x++hanya dapat dievaluasi pada saat dijalankan. Untuk mengatasi ini, standar C ++ menentukan bahwa operan sizeoftidak dievaluasi. Standar C mengatakan:

Jika tipe operan sizeofadalah tipe array panjang variabel, operan dievaluasi; jika tidak, operan tidak dievaluasi dan hasilnya adalah konstanta integer.

Edward Karak
sumber
Tidak ada VLA di C ++.
LF
-2

sizeof() operator memberikan ukuran tipe data saja, tidak mengevaluasi elemen dalam.

munna
sumber
Ini salah, sizeof()operator bertindak secara rekursif, dan akan mendapatkan ukuran dalam byte dari semua elemen wadah, anggota kelas atau struktur, dll. Anda dapat membuktikan ini sendiri dengan sangat mudah dengan membuat kelas sederhana dengan beberapa anggota dan memanggilnya sizeof(). (Namun, apa pun di sana yang merupakan pointer tidak dapat melihat ukuran - hanya ukuran pointer.) Ini terjadi semua pada waktu kompilasi, seperti komentator lain menyatakan: ekspresi di dalam sizeof()tidak dievaluasi.
Tyler Shellberg