Saya mencoba memahami perbedaan antara memcpy()
dan memmove()
, dan saya telah membaca teks yang memcpy()
tidak menangani sumber dan tujuan yang tumpang tindih sedangkan yang memmove()
tidak.
Namun, ketika saya menjalankan kedua fungsi ini pada blok memori yang tumpang tindih, keduanya memberikan hasil yang sama. Misalnya, ambil contoh MSDN berikut di memmove()
halaman bantuan: -
Apakah ada contoh yang lebih baik untuk memahami kelemahan memcpy
dan bagaimana memmove
menyelesaikannya?
// crt_memcpy.c
// Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle
// it correctly.
#include <memory.h>
#include <string.h>
#include <stdio.h>
char str1[7] = "aabbcc";
int main( void )
{
printf( "The string: %s\n", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %s\n", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
}
Keluaran:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
memcpy
akanassert
bahwa daerah tidak tumpang tindih bukan sengaja menutupi bug dalam kode Anda.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Jawaban:
Saya tidak sepenuhnya terkejut bahwa contoh Anda tidak menunjukkan perilaku aneh. Coba salin
str1
kestr1+2
gantinya dan lihat apa yang terjadi kemudian. (Mungkin tidak benar-benar membuat perbedaan, tergantung pada kompiler / perpustakaan.)Secara umum, memcpy diimplementasikan dengan cara yang sederhana (tapi cepat). Secara sederhana, itu hanya loop atas data (dalam urutan), menyalin dari satu lokasi ke lokasi lain. Ini dapat menyebabkan sumber ditimpa saat sedang dibaca.
Memmove melakukan lebih banyak pekerjaan untuk memastikannya menangani tumpang tindih dengan benar.
EDIT:
(Sayangnya, saya tidak dapat menemukan contoh yang layak, tetapi ini akan berhasil). Bandingkan implementasi memcpy dan memmove di sini. memcpy just loop, sementara memmove melakukan tes untuk menentukan arah mana yang harus di-loop agar tidak merusak data. Implementasi ini agak sederhana. Sebagian besar implementasi berkinerja tinggi lebih rumit (melibatkan menyalin blok ukuran kata pada suatu waktu daripada byte).
sumber
memmove
panggilanmemcpy
dalam satu cabang setelah menguji petunjuk: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/…memcpy
bisa lebih cepat.Memori dalam
memcpy
tidak bisa tumpang tindih atau Anda berisiko perilaku tidak terdefinisi, sedangkan memori dalammemmove
bisa tumpang tindih.Beberapa implementasi memcpy mungkin masih berfungsi untuk input yang tumpang tindih tetapi Anda tidak dapat menghitung perilaku itu. Sementara memmove harus memungkinkan untuk tumpang tindih.
sumber
Hanya karena
memcpy
tidak harus berurusan dengan daerah yang tumpang tindih, tidak berarti itu tidak berurusan dengan mereka dengan benar. Panggilan dengan wilayah yang tumpang tindih menghasilkan perilaku yang tidak terdefinisi. Perilaku tidak terdefinisi dapat bekerja sepenuhnya seperti yang Anda harapkan pada satu platform; itu tidak berarti itu benar atau valid.sumber
memcpy
diterapkan dengan cara yang persis samamemmove
. Artinya, siapa pun yang menulis kompiler tidak repot menulismemcpy
fungsi yang unik .Baik memcpy dan memove melakukan hal serupa.
Tetapi untuk melihat satu perbedaan:
memberi:
sumber
Demo Anda tidak mengekspos kelemahan memcpy karena kompiler "buruk", itu membantu Anda dalam versi Debug. Versi rilis, bagaimanapun, memberi Anda output yang sama, tetapi karena optimasi.
Register di
%eax
sini berfungsi sebagai penyimpanan sementara, yang "secara elegan" memperbaiki masalah yang tumpang tindih.Kekurangannya muncul ketika menyalin 6 byte, well, setidaknya sebagian.
Keluaran:
Terlihat aneh, itu disebabkan oleh optimasi juga.
Inilah sebabnya saya selalu memilih
memmove
ketika mencoba menyalin 2 blok memori yang tumpang tindih.sumber
Perbedaan antara
memcpy
danmemmove
itudi
memmove
, memori sumber ukuran tertentu disalin ke buffer lalu dipindahkan ke tujuan. Jadi jika ingatannya tumpang tindih, tidak ada efek samping.dalam hal
memcpy()
, tidak ada buffer tambahan yang diambil untuk memori sumber. Penyalinan dilakukan langsung pada memori sehingga ketika ada memori yang tumpang tindih, kami mendapatkan hasil yang tidak terduga.Ini dapat diamati dengan kode berikut:
Output adalah:
sumber
Seperti yang telah ditunjukkan dalam jawaban lain,
memmove
lebih canggih daripadamemcpy
yang menyebabkan tumpang tindih memori. Hasil memmove didefinisikan seolah-olahsrc
disalin ke dalam buffer dan kemudian disalin ke dalam bufferdst
. Ini TIDAK berarti bahwa implementasi aktual menggunakan buffer apa pun, tetapi mungkin melakukan beberapa aritmatika pointer.sumber
kompiler dapat mengoptimalkan memcpy, misalnya:
Memcpy ini dapat dioptimalkan sebagai:
x = *(int*)some_pointer;
sumber
int
akses yang tidak selaras . Pada beberapa arsitektur (misalnya Cortex-M0), berusaha mengambil 32-bitint
dari alamat yang bukan kelipatan empat akan menyebabkan crash (tetapimemcpy
akan berfungsi). Jika seseorang akan menggunakan CPU yang memungkinkan akses yang tidak selaras atau menggunakan kompiler dengan kata kunci yang mengarahkan kompiler untuk merakit integer dari byte yang diambil secara terpisah jika diperlukan, seseorang dapat melakukan sesuatu seperti#define UNALIGNED __unaligned
dan kemudian `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
Tetapi beberapa melakukannya, karena mereka memperbaiki salinan selama kesalahan. Saya bekerja pada sistem seperti ini, dan butuh sedikit waktu untuk memahami mengapa kinerjanya sangat buruk.*(int *)some_pointer
adalah pelanggaran alias ketat, tetapi Anda mungkin berarti bahwa kompiler akan menampilkan perakitan yang menyalin intKode yang diberikan dalam tautan http://clc-wiki.net/wiki/memcpy untuk memcpy agak membingungkan saya, karena tidak memberikan output yang sama ketika saya menerapkannya menggunakan contoh di bawah ini.
Output:
Tapi sekarang Anda bisa mengerti mengapa memmove akan menangani masalah yang tumpang tindih.
sumber
C11 draft standar
The C11 N1570 standar rancangan mengatakan:
7.24.2.1 "Fungsi memcpy":
7.24.2.2 "Fungsi memmove":
Oleh karena itu, setiap tumpang tindih
memcpy
mengarah pada perilaku yang tidak terdefinisi, dan apa pun bisa terjadi: buruk, tidak ada, atau bahkan baik. Bagus jarang terjadi :-)memmove
Namun jelas mengatakan bahwa semuanya terjadi seolah-olah buffer perantara digunakan, jadi jelas tumpang tindih OK.C ++
std::copy
lebih memaafkan, dan memungkinkan tumpang tindih: Apakah std :: copy menangani rentang yang tumpang tindih?sumber
memmove
gunakan array sementara ekstra n, jadi apakah itu menggunakan memori tambahan? Tapi bagaimana bisa jika kita belum memberikannya akses ke memori apa pun. (Menggunakan memori 2x).Saya telah mencoba menjalankan program yang sama menggunakan eclipse dan itu menunjukkan perbedaan yang jelas antara
memcpy
danmemmove
.memcpy()
tidak peduli tentang tumpang tindih lokasi memori yang mengakibatkan kerusakan data, sementaramemmove()
akan menyalin data ke variabel sementara terlebih dahulu dan kemudian menyalin ke lokasi memori aktual.Saat mencoba menyalin data dari lokasi
str1
kestr1+2
, output darimemcpy
adalah "aaaaaa
". Pertanyaannya bagaimana?memcpy()
akan menyalin satu byte pada satu waktu dari kiri ke kanan. Seperti yang ditunjukkan dalam program Anda "aabbcc
" maka semua penyalinan akan terjadi seperti di bawah ini,aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
akan menyalin data ke variabel sementara terlebih dahulu dan kemudian menyalin ke lokasi memori aktual.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
Output adalah
memcpy
:aaaaaa
memmove
:aaaabb
sumber
memmove()
salinan itu ke lokasi perantara. Seharusnya hanya menyalin secara terbalik bila perlu.