C dan C ++ memiliki banyak perbedaan, dan tidak semua kode C yang valid adalah kode C ++ yang valid.
(Dengan "valid" maksud saya kode standar dengan perilaku yang ditentukan, yaitu tidak spesifik implementasi / tidak terdefinisi / dll.)
Apakah ada skenario di mana sepotong kode valid dalam C dan C ++ akan menghasilkan perilaku yang berbeda ketika dikompilasi dengan kompiler standar di setiap bahasa?
Untuk menjadikannya perbandingan yang masuk akal / berguna (saya mencoba mempelajari sesuatu yang praktis berguna, bukan mencoba menemukan celah yang jelas dalam pertanyaan), mari kita asumsikan:
- Tidak ada yang terkait dengan preprosesor (yang berarti tidak ada peretasan
#ifdef __cplusplus
, pragma, dll.) - Implementasi apa pun yang didefinisikan adalah sama di kedua bahasa (misalnya batas numerik, dll.)
- Kami membandingkan versi terbaru dari setiap standar (mis. Katakan, C ++ 98 dan C90 atau lebih baru).
Jika versi penting, maka tolong sebutkan versi masing-masing yang menghasilkan perilaku yang berbeda.
Jawaban:
Berikut ini, valid dalam C dan C ++, akan (kemungkinan besar) menghasilkan nilai yang berbeda di
i
dalam C dan C ++:Lihat Ukuran karakter ('a') di C / C ++ untuk penjelasan perbedaannya.
Satu lagi dari artikel ini :
sumber
struct
sebelum nama struct.struct sz { int i[2];};
berarti bahwa C dan C ++ harus menghasilkan nilai yang berbeda. (Sedangkan DSP dengan sizeof (int) == 1, dapat menghasilkan nilai yang sama).Berikut adalah contoh yang mengambil keuntungan dari perbedaan antara pemanggilan fungsi dan deklarasi objek dalam C dan C ++, serta fakta bahwa C90 memungkinkan pemanggilan fungsi yang tidak dideklarasikan:
Dalam C ++ ini tidak akan mencetak apa-apa karena sementara
f
dibuat dan dihancurkan, tetapi dalam C90 ini akan mencetakhello
karena fungsi dapat dipanggil tanpa dideklarasikan.Jika Anda bertanya-tanya tentang nama
f
yang digunakan dua kali, standar C dan C ++ secara eksplisit memungkinkan ini, dan untuk membuat objek Anda harus mengatakanstruct f
untuk ambigu jika Anda ingin struktur, atau tinggalkanstruct
jika Anda ingin fungsi.sumber
Untuk C ++ vs. C90, setidaknya ada satu cara untuk mendapatkan perilaku berbeda yang tidak didefinisikan implementasi. C90 tidak memiliki komentar single-line. Dengan sedikit perhatian, kita dapat menggunakannya untuk membuat ekspresi dengan hasil yang sepenuhnya berbeda di C90 dan di C ++.
Di C ++, segala sesuatu dari
//
hingga akhir baris adalah komentar, jadi ini berfungsi sebagai:Karena C90 tidak memiliki komentar satu baris, hanya
/* comment */
itu yang merupakan komentar. Yang pertama/
dan2
yang kedua adalah bagian dari inisialisasi, sehingga keluar ke:Jadi, kompiler C ++ yang benar akan memberikan 13, tetapi kompiler C90 yang benar-benar benar 8. Tentu saja, saya hanya memilih angka acak di sini - Anda dapat menggunakan angka lain sesuai keinginan Anda.
sumber
2
, itu akan dibaca sebagai10 / + 3
yang valid (unary +).C90 vs. C ++ 11 (
int
vs.double
):Dalam C
auto
berarti variabel lokal. Di C90 tidak masalah untuk menghilangkan variabel atau tipe fungsi. Defaultnya adalahint
. Dalam C ++ 11auto
berarti sesuatu yang sama sekali berbeda, ia memberitahu kompiler untuk menyimpulkan jenis variabel dari nilai yang digunakan untuk menginisialisasi itu.sumber
auto
?int
secara default. Ini pintar! +1int
.int
. Namun, di dunia nyata, di mana ada banyak kode warisan dan pemimpin pasar masih belum menerapkan C99 dan tidak memiliki niat untuk melakukannya, pembicaraan tentang "versi C yang usang" adalah tidak masuk akal.Contoh lain yang belum saya lihat disebutkan, yang satu ini menyoroti perbedaan preprosesor:
Ini mencetak "false" dalam C dan "true" di C ++ - Di C, setiap makro tak terdefinisi mengevaluasi ke 0. Dalam C ++, ada 1 pengecualian: "true" mengevaluasi ke 1.
sumber
#define true false
ಠ_ಠPer standar C ++ 11:
Sebuah. Operator koma melakukan konversi nilai-ke-nilai dalam C tetapi tidak pada C ++:
Di C ++ nilai ekspresi ini akan menjadi 100 dan di C ini akan menjadi
sizeof(char*)
.b. Dalam C ++ jenis enumerator adalah enumnya. Dalam C tipe enumerator adalah int.
Ini berarti bahwa
sizeof(int)
mungkin tidak sama dengansizeof(E)
.c. Dalam C ++ fungsi yang dideklarasikan dengan daftar params kosong tidak membutuhkan argumen. Dalam daftar params C kosong berarti bahwa jumlah dan jenis params fungsi tidak diketahui.
sumber
sizeof(char*)
bisa 100 dalam hal contoh pertama akan menghasilkan perilaku yang dapat diamati yang sama dalam C dan C ++ (yaitu meskipun metode memperolehs
akan berbeda,s
akhirnya akan menjadi 100). OP menyebutkan bahwa jenis perilaku yang ditentukan implementasi ini baik-baik saja karena ia hanya ingin menghindari jawaban pengacara bahasa, jadi yang pertama baik-baik saja dengan pengecualiannya. Tapi yang kedua bagus dalam hal apa pun.char arr[sizeof(char*)+1]; int s = sizeof(0, arr);
void *arr[100]
. Dalam hal ini elemen berukuran sama dengan pointer ke elemen yang sama, jadi selama ada 2 elemen atau lebih, array harus lebih besar dari alamat elemen pertamanya.Program ini mencetak
1
dalam C ++ dan0
dalam C:Ini terjadi karena ada
double abs(double)
kelebihan dalam C ++, jadiabs(0.6)
kembali0.6
sementara di C itu kembali0
karena konversi double-to-int implisit sebelum memohonint abs(int)
. Di C, Anda harus menggunakannyafabs
untuk bekerja dengannyadouble
.sumber
stdlib.h
hanya mendefinisikanabs(int)
danabs(long)
; versiabs(double)
ini dinyatakan olehmath.h
. Jadi program ini masih dapat memanggilabs(int)
versi. Ini adalah detail implementasi apakahstdlib.h
juga menyebabkanmath.h
untuk dimasukkan. (Saya pikir itu akan menjadi bug jikaabs(double)
dipanggil, tetapi aspek lain darimath.h
tidak termasuk).<math.h>
juga termasuk kelebihan beban tambahan; dalam praktek ternyata semua kompiler utama tidak termasuk kelebihan itu kecuali formulir<cmath>
digunakan.Dalam C, ini mencetak berapa pun nilainya
sizeof(int)
pada sistem saat ini, yang biasanya4
di sebagian besar sistem yang umum digunakan saat ini.Di C ++, ini harus dicetak 1.
sumber
%d
bukan specifier format yang tepat untuksize_t
.sizeof
Perangkap lain : ekspresi boolean.Itu sama dengan
sizeof(int)
di C, karena ekspresi adalah tipeint
, tetapi biasanya 1 di C ++ (meskipun tidak harus). Dalam praktiknya mereka hampir selalu berbeda.sumber
!
harus cukup untuk abool
.sizeof(0)
ada4
di C dan C ++ karena0
merupakan nilai integer.sizeof(!0)
ada4
di C dan1
di C ++. Logical NOT beroperasi pada operan tipe bool. Jika nilai int0
secara implisit dikonversi kefalse
(nilai bool), maka dibalik, menghasilkantrue
. Keduanyatrue
danfalse
merupakan nilai-nilai bool di C ++ dansizeof(bool)
itu1
. Namun dalam C!0
mengevaluasi1
, yang merupakan nilai dari tipe int. Bahasa pemrograman C tidak memiliki tipe data bool secara default.Bahasa Pemrograman C ++ (Edisi ke-3) memberikan tiga contoh:
sizeof ('a'), seperti yang disebutkan @Adam Rosenfield;
//
komentar yang digunakan untuk membuat kode tersembunyi:Struktur, dll. Menyembunyikan barang di luar lingkup, seperti pada contoh Anda.
sumber
Berangan tua yang tergantung pada kompiler C, tidak mengenali komentar akhir C ++ ...
sumber
Satu lagi terdaftar oleh Standar C ++:
sumber
x
di atas. Saya pikir Anda mengatakan "arraya
".Fungsi sebaris di C default ke lingkup eksternal sedangkan yang di C ++ tidak.
Mengkompilasi dua file berikut bersama-sama akan mencetak "I am inline" untuk GNU C tetapi tidak untuk C ++.
File 1
File 2
Juga, C ++ secara implisit memperlakukan
const
global apa punstatic
kecuali jika dinyatakan secara eksplisitextern
, tidak seperti C yangextern
merupakan default.sumber
extern
yang ditunjukkan di sini?struct fun
vsfn
) dan tidak ada hubungannya apakah fungsi tersebut sebaris. Hasilnya identik jika Anda menghapusinline
kualifikasi.inline
tidak ditambahkan hingga C99, tetapi dalam C99fun()
tidak dapat dipanggil tanpa prototipe dalam cakupan. Jadi saya menganggap jawaban ini hanya berlaku untuk GNU C.Kembali dengan kode keluar 0 di C ++, atau 3 di C.
Trik ini mungkin bisa digunakan untuk melakukan sesuatu yang lebih menarik, tetapi saya tidak bisa memikirkan cara yang baik untuk membuat konstruktor yang cocok untuk C. Saya mencoba membuat contoh yang sama membosankannya dengan copy constructor, yang akan membuat argumen dilewati, meskipun dengan cara yang agak tidak portabel:
VC ++ 2005 menolak untuk mengkompilasi bahwa dalam mode C ++, mengeluh tentang bagaimana "kode keluar" didefinisikan ulang. (Saya pikir ini adalah kompiler bug, kecuali jika saya tiba-tiba lupa bagaimana memprogramnya.) Ia keluar dengan kode keluar proses 1 saat dikompilasi sebagai C sekalipun.
sumber
exit(code)
adalah deklarasi yang valid dari variabelcode
jenisexit
, rupanya. (Lihat "parsing yang paling menjengkelkan", yang merupakan masalah yang berbeda namun serupa).Program ini mencetak
128
(32 * sizeof(double)
) ketika dikompilasi menggunakan kompiler C ++ dan4
ketika dikompilasi menggunakan kompiler C.Ini karena C tidak memiliki gagasan tentang resolusi ruang lingkup. Dalam struktur C yang terkandung dalam struktur lain bisa dimasukkan ke dalam ruang lingkup struktur luar.
sumber
32*sizeof(double)
alih - alih 32) :)size_t
dengan%d
Jangan lupa perbedaan antara ruang nama global C dan C ++. Misalkan Anda memiliki foo.cpp
dan foo2.c
Sekarang anggaplah Anda memiliki main.c dan main.cpp yang keduanya terlihat seperti ini:
Ketika dikompilasi sebagai C ++, ia akan menggunakan simbol dalam namespace global C ++; di C akan menggunakan C satu:
sumber
foo
). Tidak ada "ruang nama global" yang terpisah.Ini agak aneh karena valid dalam C ++ dan C99, C11, dan C17 (meskipun opsional dalam C11, C17); tapi tidak berlaku di C89.
Dalam C99 + ia menciptakan array panjang variabel, yang memiliki kekhasan tersendiri terhadap array normal, karena memiliki tipe runtime, bukan tipe waktu kompilasi, dan
sizeof array
bukan ekspresi konstanta bilangan bulat dalam C. Pada C ++ jenisnya sepenuhnya statis.Jika Anda mencoba menambahkan inisialisasi di sini:
valid C ++ tetapi bukan C, karena array panjang variabel tidak dapat memiliki initializer.
sumber
Ini menyangkut nilai dan nilai dalam C dan C ++.
Dalam bahasa pemrograman C, operator pre-increment dan post-increment mengembalikan nilai, bukan nilai. Ini berarti bahwa mereka tidak dapat berada di sisi kiri
=
operator penugasan. Kedua pernyataan ini akan memberikan kesalahan kompilator di C:Namun dalam C ++, operator pra-kenaikan mengembalikan nilai , sedangkan operator pasca kenaikan mengembalikan nilai. Ini berarti bahwa ekspresi dengan operator pra-kenaikan dapat ditempatkan di sisi kiri
=
operator penugasan!Sekarang mengapa demikian? Post-increment menambah variabel, dan mengembalikan variabel seperti sebelum kenaikan terjadi. Ini sebenarnya hanya nilai. Nilai sebelumnya dari variabel a disalin ke dalam register sebagai sementara, dan kemudian a bertambah. Tetapi nilai sebelumnya dari a dikembalikan oleh ekspresi, itu adalah nilai. Itu tidak lagi mewakili konten variabel saat ini.
Pre-increment pertama-tama akan menambah variabel, dan kemudian mengembalikan variabel sesuai dengan yang terjadi setelah kenaikan terjadi. Dalam hal ini, kita tidak perlu menyimpan nilai lama dari variabel ke register sementara. Kami hanya mengambil nilai baru dari variabel setelah ditambahkan. Jadi pre-increment mengembalikan nilai, ia mengembalikan variabel itu sendiri. Kita dapat menggunakan nilai ini untuk sesuatu yang lain, seperti pernyataan berikut. Ini adalah konversi implisit nilai menjadi nilai.
Karena pra-kenaikan mengembalikan nilai, kami juga dapat menetapkan sesuatu untuknya. Dua pernyataan berikut ini identik. Dalam penugasan kedua, pertama a bertambah, kemudian nilai baru ditimpa dengan 2.
sumber
Struktur kosong memiliki ukuran 0 dalam C dan 1 dalam C ++:
sumber