Hari ini saya mengajar beberapa teman bagaimana cara menggunakan Cs struct
. Salah satu dari mereka bertanya apakah Anda dapat mengembalikan sebuah struct
fungsi, yang saya jawab: "Tidak! Anda akan mengembalikan pointer ke malloc
ed secara dinamis struct
."
Berasal dari seseorang yang terutama melakukan C ++, saya berharap tidak dapat mengembalikan struct
nilai s. Dalam C ++ Anda bisa membebani operator =
objek Anda secara berlebihan dan masuk akal untuk memiliki fungsi untuk mengembalikan objek Anda berdasarkan nilai. Namun, dalam C, Anda tidak memiliki opsi itu dan itu membuat saya berpikir apa yang sebenarnya dilakukan oleh kompiler. Pertimbangkan yang berikut ini:
struct MyObj{
double x, y;
};
struct MyObj foo(){
struct MyObj a;
a.x = 10;
a.y = 10;
return a;
}
int main () {
struct MyObj a;
a = foo(); // This DOES work
struct b = a; // This does not work
return 0;
}
Saya mengerti mengapa struct b = a;
tidak bekerja - Anda tidak bisa kelebihan operator =
untuk tipe data Anda. Bagaimana a = foo();
kompilasi itu baik-baik saja? Apakah ini berarti sesuatu selain struct b = a;
? Mungkin pertanyaan yang ingin diajukan adalah: Apa yang sebenarnya dilakukan oleh return
pernyataan bersama =
?
[Sunting]: Ok, saya hanya menunjuk struct b = a
adalah kesalahan sintaksis - itu benar dan saya idiot! Tapi itu membuatnya semakin rumit! Penggunaan struct MyObj b = a
memang berhasil! Apa yang kulewatkan di sini?
struct b = a;
adalah kesalahan sintaksis. Bagaimana jika Anda mencobastruct MyObj b = a;
?struct MyObj b = a;
tampaknya berhasil :)Jawaban:
Anda dapat mengembalikan struktur dari suatu fungsi (atau menggunakan
=
operator) tanpa masalah. Ini adalah bagian bahasa yang didefinisikan dengan baik. Satu-satunya masalahstruct b = a
adalah Anda tidak memberikan jenis yang lengkap.struct MyObj b = a
akan bekerja dengan baik. Anda juga bisa meneruskan struktur ke fungsi - struktur persis sama dengan tipe bawaan apa pun untuk keperluan pemindahan parameter, nilai pengembalian, dan penugasan.Berikut adalah program demonstrasi sederhana yang melakukan ketiganya - melewatkan struktur sebagai parameter, mengembalikan struktur dari suatu fungsi, dan menggunakan struktur dalam pernyataan penugasan:
Contoh berikutnya hampir persis sama, tetapi menggunakan
int
tipe bawaan untuk tujuan demonstrasi. Kedua program memiliki perilaku yang sama sehubungan dengan pass-by-value untuk passing parameter, tugas, dll .:sumber
Saat melakukan panggilan seperti itu
a = foo();
, kompiler mungkin mendorong alamat struktur hasil pada stack dan meneruskannya sebagai pointer "tersembunyi" kefoo()
fungsi. Secara efektif, itu bisa menjadi sesuatu seperti:Namun, implementasi yang tepat dari ini tergantung pada kompiler dan / atau platform. Seperti yang dicatat oleh Carl Norum, jika strukturnya cukup kecil, itu mungkin bahkan dikembalikan sepenuhnya dalam register.
sumber
foo
bingkai tumpukan. Itu harus di tempat yang bertahan melewati kembalinyafoo
.foo
. Di dalam fungsifoo
, Anda hanya melakukan tugas*r = a
akhirnya akan (secara efektif) melakukan salinan variabel lokal ke variabel pemanggil. Saya mengatakan "secara efektif" karena kompiler dapat mengimplementasikan RVO dan menghilangkan variabel lokala
sepenuhnya.c return struct
: mereka tahu bahwa dalam cdecleax
dikembalikan oleh nilai dan struct pada umumnya tidak cocok di dalameax
. Ini yang saya cari.The
struct b
garis tidak bekerja karena itu kesalahan sintaks. Jika Anda mengembangkannya untuk memasukkan tipe itu akan berfungsi dengan baikApa yang dilakukan C di sini pada dasarnya adalah
memcpy
dari struct sumber ke tujuan. Ini berlaku untuk penugasan dan pengembalianstruct
nilai (dan benar-benar setiap nilai lain dalam C)sumber
memcpy
dalam kasus ini - setidaknya, jika strukturnya cukup besar.memcpy
fungsi untuk situasi struktur. Anda dapat membuat program pengujian cepat dan melihat GCC melakukannya, misalnya. Untuk tipe bawaan yang tidak akan terjadi - mereka tidak cukup besar untuk memicu optimasi semacam itu.memcpy
simbol yang ditentukan, jadi kami sering mengalami kesalahan linker "simbol tidak terdefinisi" ketika kompiler memutuskan untuk memuntahkannya sendiri.ya, mungkin kita bisa melewati struktur dan mengembalikan struktur juga. Anda benar tetapi sebenarnya Anda tidak melewati tipe data yang seharusnya seperti struct ini MyObj b = a.
Sebenarnya saya juga mengetahui ketika saya mencoba mencari solusi yang lebih baik untuk mengembalikan lebih dari satu nilai untuk fungsi tanpa menggunakan pointer atau variabel global.
Sekarang di bawah ini adalah contoh untuk hal yang sama, yang menghitung penyimpangan nilai siswa tentang rata-rata.
sumber
Sejauh yang saya ingat, versi pertama C hanya diperbolehkan untuk mengembalikan nilai yang bisa masuk ke dalam register prosesor, yang berarti bahwa Anda hanya bisa mengembalikan pointer ke struct. Pembatasan yang sama diterapkan pada argumen fungsi.
Versi yang lebih baru memungkinkan untuk mengedarkan objek data yang lebih besar seperti struct. Saya pikir fitur ini sudah umum selama tahun delapan puluhan atau awal sembilan puluhan.
Array, bagaimanapun, masih bisa dilewati dan dikembalikan hanya sebagai pointer.
sumber
Anda dapat menetapkan struct dalam C.
a = b;
sintaks yang valid.Anda cukup meninggalkan bagian dari jenis - tag struct - di baris Anda yang tidak berfungsi.
sumber
Tidak ada masalah dalam melewati kembali struct. Itu akan diteruskan oleh nilai
Tetapi, bagaimana jika struct berisi anggota yang memiliki alamat variabel lokal
Sekarang, di sini e1.name berisi alamat memori lokal ke fungsi get (). Setelah mendapatkan () kembali, alamat lokal untuk nama akan dibebaskan. SO, di pemanggil jika kami mencoba mengakses alamat itu, itu dapat menyebabkan kesalahan segmentasi, karena kami mencoba alamat yang dibebaskan. Itu buruk..
Sedangkan e1.id akan benar-benar valid karena nilainya akan disalin ke e2.id
Jadi, kita harus selalu berusaha menghindari mengembalikan alamat memori lokal suatu fungsi.
Apa pun yang ditransfer dapat dikembalikan kapan saja
sumber
berfungsi dengan baik dengan versi kompiler yang lebih baru. Sama seperti id, konten nama akan disalin ke variabel struktur yang ditugaskan.
sumber
alamat struct var e2 didorong sebagai arg to callee stack dan nilai ditugaskan di sana. Bahkan, get () mengembalikan alamat e2 di eax reg. Ini berfungsi seperti panggilan dengan referensi.
sumber