Tetapkan satu struct ke yang lain di C

146

Bisakah Anda menetapkan satu instance dari struct ke yang lain, seperti:

struct Test t1;
struct Test t2;
t2 = t1;

Saya telah melihatnya bekerja untuk struktur sederhana, tetapi apakah itu bekerja untuk struktur yang kompleks?
Bagaimana kompiler mengetahui cara menyalin item data tergantung pada jenisnya, yaitu membedakan antara intstring dan?

shreyasva
sumber

Jawaban:

151

Ya jika strukturnya dari jenis yang sama. Pikirkan itu sebagai salinan memori.

fabrizioM
sumber
72
Perlu diingat bahwa tidak ada salinan dalam, menunjuk ke memori tidak disalin.
Georg Schölly
3
Concurrency juga menjadi masalah di sini.
Tim Post
16
@Tim Concurrency tidak lebih merupakan masalah untuk penugasan tipe bawaan, seperti bilangan bulat dan ganda - penugasan juga bukan operasi atom untuk keduanya.
2
OK, jika ada salinan yang dibuat, dapatkah saya membebaskan memori nanti dengan free ()?
Betlista
5
@Betlista Anda tidak dapat mengosongkan memori dengan free () karena mereka adalah variabel otomatis: en.wikipedia.org/wiki/Automatic_variable
joshdoe
138

Ya, tugas didukung untuk struct. Namun, ada masalah:

struct S {
   char * p;
};

struct S s1, s2;
s1.p = malloc(100);
s2 = s1;

Sekarang pointer dari kedua struct menunjuk ke blok memori yang sama - kompiler tidak menyalin data yang ditunjuk. Sekarang sulit untuk mengetahui instance struct mana yang memiliki data. Inilah sebabnya mengapa C ++ menemukan konsep operator penugasan yang dapat ditentukan pengguna - Anda dapat menulis kode khusus untuk menangani kasus ini.


sumber
1
Saya menaikkannya karena membacanya membuat saya menyadari kesalahan / kelalaian dalam jawaban saya sendiri.
Clifford
1
+1 untuk mencatat bahwa sebenarnya tidak ada penyalinan yang terjadi.
Tom Duckering
14
Mengapa ini ditandai sebagai spam? Adakah yang kehilangan kendali atas mouse mereka?
Georg Fritzsche
@ Gf Dan tampaknya juga ofensif!
2
@rahmanisback Jawaban anon cukup jelas tentang topik ini: "kompiler tidak menyalin menunjuk ke data". Data structitu sendiri dengan jelas disalin.
Tobias
24

Lihat Pertama pada contoh ini:

Kode C untuk program C sederhana diberikan di bawah ini

struct Foo {
    char a;
    int b;
    double c;
    } foo1,foo2;

void foo_assign(void)
{
    foo1 = foo2;
}
int main(/*char *argv[],int argc*/)
{
    foo_assign();
return 0;
}

Kode ASM Setara untuk foo_assign () adalah

00401050 <_foo_assign>:
  401050:   55                      push   %ebp
  401051:   89 e5                   mov    %esp,%ebp
  401053:   a1 20 20 40 00          mov    0x402020,%eax
  401058:   a3 30 20 40 00          mov    %eax,0x402030
  40105d:   a1 24 20 40 00          mov    0x402024,%eax
  401062:   a3 34 20 40 00          mov    %eax,0x402034
  401067:   a1 28 20 40 00          mov    0x402028,%eax
  40106c:   a3 38 20 40 00          mov    %eax,0x402038
  401071:   a1 2c 20 40 00          mov    0x40202c,%eax
  401076:   a3 3c 20 40 00          mov    %eax,0x40203c
  40107b:   5d                      pop    %ebp
  40107c:   c3                      ret    

Seperti yang Anda lihat bahwa tugas hanya diganti dengan instruksi "mov" dalam perakitan, operator penugasan berarti memindahkan data dari satu lokasi memori ke lokasi memori lain. Tugas hanya akan melakukannya untuk anggota langsung dari suatu struktur dan akan gagal untuk menyalin ketika Anda memiliki tipe data kompleks dalam suatu struktur. Di sini COMPLEX berarti Anda tidak dapat memiliki array pointer, menunjuk ke daftar.

Array karakter dalam struktur itu sendiri tidak akan berfungsi pada kebanyakan kompiler, ini karena tugas hanya akan mencoba untuk menyalin tanpa melihat tipe data menjadi tipe kompleks.

Arun Kaushal
sumber
2
Dapatkah Anda menguraikan kondisi mana yang akan gagal karena tampaknya selalu bekerja untuk saya
AlphaGoku
15

Ini adalah salinan sederhana, seperti yang akan Anda lakukan memcpy()(memang, beberapa kompiler benar-benar menghasilkan panggilan memcpy()untuk kode itu). Tidak ada "string" di C, hanya pointer ke sekelompok a chars. Jika struktur sumber Anda berisi pointer seperti itu, maka pointer akan disalin, bukan karakter itu sendiri.

Thomas Pornin
sumber
OK, jadi kompiler menerjemahkan ini ke memcpy, lihat di sini: godbolt.org/z/nPxqWc - Tapi sekarang jika saya melewati pointer identik adan b, dan *a = *bditerjemahkan ke memcpyperilaku yang tidak terdefinisi, karena untuk memcpy"Area memori tidak boleh tumpang tindih." (mengutip dari halaman manual). Jadi apakah kompiler salah dalam menggunakan memcpyatau saya salah dalam menulis tugas seperti itu?
bukan-pengguna
6

Apakah maksud Anda "Kompleks" seperti dalam bilangan kompleks dengan bagian nyata dan imajiner? Ini sepertinya tidak mungkin, jadi jika tidak Anda harus memberikan contoh karena "kompleks" tidak berarti spesifik dalam hal bahasa C.

Anda akan mendapatkan salinan memori langsung dari struktur; apakah itu yang Anda inginkan tergantung pada strukturnya. Misalnya jika struktur berisi pointer, kedua salinan akan menunjuk ke data yang sama. Ini mungkin atau mungkin bukan yang Anda inginkan; itu tergantung pada desain program Anda.

Untuk melakukan salinan 'pintar' (atau salinan 'dalam'), Anda perlu mengimplementasikan fungsi untuk melakukan salinan. Ini bisa sangat sulit untuk dicapai jika struktur itu sendiri mengandung pointer dan struktur yang juga mengandung pointer, dan mungkin pointer ke struktur seperti itu (mungkin itu yang Anda maksud dengan "kompleks"), dan sulit untuk mempertahankannya. Solusi sederhana adalah dengan menggunakan C ++ dan mengimplementasikan copy constructor dan operator penugasan untuk setiap struktur atau kelas, maka masing-masing menjadi bertanggung jawab atas semantik salinannya sendiri, Anda dapat menggunakan sintaks penugasan, dan lebih mudah dipelihara.

Clifford
sumber