Dapatkah saya memanggil memcpy () dan memmove () dengan "jumlah byte" disetel ke nol?

103

Apakah saya perlu menangani kasus ketika saya sebenarnya tidak memiliki apa pun untuk dipindahkan / disalin dengan memmove()/ memcpy()sebagai kasus tepi

int numberOfBytes = ...
if( numberOfBytes != 0 ) {
    memmove( dest, source, numberOfBytes );
}

atau haruskah saya memanggil fungsi tanpa memeriksa

int numberOfBytes = ...
memmove( dest, source, numberOfBytes );

Apakah perlu memeriksa di cuplikan sebelumnya?

gigi tajam
sumber
6
Pertanyaan mengingatkan saya sedikit untuk memeriksa petunjuk nol pada fungsi seperti gratis. Tidak perlu, tapi saya akan memberi komentar di sana untuk menunjukkan pemikiran Anda tentang hal itu.
Toad
12
@ Beban: Tujuan apa yang dilayaninya, selain untuk mengacaukan kode? Ketika membaca kode seseorang, saya tidak perlu tahu bahwa programmer asli "berpikir untuk melakukan operasi ini yang sebenarnya tidak perlu, tetapi karena itu tidak perlu, saya tidak melakukannya". Jika saya melihat pointer dibebaskan, saya tahu itu diperbolehkan menjadi nol, jadi saya tidak perlu mengetahui pemikiran programmer asli tentang subjek "haruskah saya memeriksa null". Dan hal yang sama berlaku untuk menyalin 0 byte denganmemcpy
jalf
9
@jalf: fakta bahwa ini adalah pertanyaan tentang stackoverflow, membuatnya menjadi sesuatu yang diragukan orang. Jadi menambahkan komentar mungkin tidak membantu Anda, tetapi mungkin membantu seseorang yang kurang berpengetahuan
Toad
2
@ Muat Ya, komentar yang secara eksplisit menyebutkan mengapa pemeriksaan yang tampaknya perlu sebenarnya tidak dapat bermanfaat pada prinsipnya. Sisi lain dari koin ini adalah bahwa contoh khusus ini adalah kasus umum yang melibatkan fungsi pustaka standar yang setiap programmer hanya perlu mempelajari jawabannya satu kali; kemudian mereka dapat mengenali dalam program apa pun yang mereka baca bahwa pemeriksaan ini tidak diperlukan. Untuk itu alasan, aku akan menghilangkan komentar. Basis kode dengan beberapa panggilan seperti ini harus menyalin dan menempelkan komentar ke masing-masing, atau menggunakannya secara sewenang-wenang hanya pada beberapa panggilan, yang keduanya jelek.
Mark Amery

Jawaban:

145

Dari standar C99 (7.21.1 / 2):

Di mana argumen yang dideklarasikan sebagai size_t nmenentukan panjang array untuk suatu fungsi, ndapat memiliki nilai nol pada panggilan ke fungsi itu. Kecuali secara eksplisit dinyatakan lain dalam deskripsi fungsi tertentu di sub-klausul ini, argumen penunjuk pada panggilan semacam itu masih memiliki nilai yang valid, seperti yang dijelaskan dalam 7.1.4. Pada panggilan seperti itu, fungsi yang menempatkan karakter tidak menemukan kemunculan, fungsi yang membandingkan dua urutan karakter menghasilkan nol, dan fungsi yang menyalin karakter menyalin karakter nol.

Jadi jawabannya tidak; pemeriksaan tidak perlu (atau ya; Anda bisa lulus nol).

Mike Seymour
sumber
1
Akankah sebuah pointer dianggap "valid" untuk tujuan fungsi seperti itu jika menunjuk ke lokasi setelah elemen terakhir dari sebuah array? Penunjuk seperti itu tidak dapat secara sah ditangguhkan, tetapi seseorang dapat dengan aman melakukan beberapa hal penunjuk-ish lain seperti mengurangi satu darinya.
supercat
1
@supercat: ya, penunjuk yang menunjuk satu melewati akhir larik valid untuk aritmatika penunjuk dengan penunjuk lain di dalam (atau satu melewati ujung) larik itu, tetapi tidak dapat dibedakan.
Mike Seymour
@MikeSeymour: Bukankah kutipan tersebut menyiratkan jawaban yang berlawanan: pemeriksaan itu diperlukan dan Anda tidak bisa lulus nol dengan petunjuk nol?
neverhoodboy
7
@neverhoodboy: Tidak, kutipan tersebut dengan jelas mengatakan " ndapat memiliki nilai nol". Anda benar bahwa Anda tidak dapat memberikan petunjuk nol, tetapi bukan itu yang ditanyakan oleh pertanyaan itu.
Mike Seymour
1
@MikeSeymour: Salahku. Sangat menyesal. Pertanyaannya adalah tentang ukuran bukan penunjuknya.
neverhoodboy
5

Seperti yang dikatakan oleh @You, standar menetapkan bahwa memcpy dan memmove harus menangani kasus ini tanpa masalah; karena mereka biasanya diterapkan entah bagaimana

void *memcpy(void *_dst, const void *_src, size_t len)
{
    unsigned char *dst = _dst;
    const unsigned char *src = _src;
    while(len-- > 0)
        *dst++ = *src++;
    return _dst;
}

Anda bahkan tidak boleh memiliki hukuman kinerja selain pemanggilan fungsi; jika kompilator mendukung intrinsik / sebaris untuk fungsi semacam itu, pemeriksaan tambahan bahkan dapat membuat kode mikro-bit-bit lebih lambat, karena pemeriksaan tersebut sudah dilakukan sementara.

Matteo Italia
sumber
1
Menurut saya, fungsi ini mungkin dibuat dalam rakitan di mana Anda dapat mengoptimalkan transfer memori jauh lebih baik daripada di c
Toad
"entah bagaimana seperti" :) Sebenarnya hampir semua implementasi yang saya lihat berada dalam perakitan, dan mencoba menyalin sebagian besar bit menggunakan ukuran kata asli (misalnya uint32_t di x86), tetapi itu tidak mengubah substansi jawabannya : ini adalah perulangan while yang tidak membutuhkan kalkulasi hebat sebelum memulai, jadi pemeriksaan sudah selesai.
Matteo Italia
9
-1, implementasi tipikal tidak relevan dengan apakah C valid untuk memanggil fungsi-fungsi ini (yang bahkan mungkin tidak diimplementasikan sebagai fungsi C) dengan argumen nol.
R .. GitHub STOP HELPING ICE
4
Fakta bahwa itu C valid telah ditutupi oleh jawaban lain, seperti yang saya katakan di awal jawaban saya: "Seperti yang dikatakan oleh @You, standar menetapkan bahwa memcpy dan memmove harus menangani kasus ini tanpa masalah". Saya baru saja menambahkan pendapat saya tentang fakta bahwa Anda seharusnya tidak takut memanggil memcpy dengan len = 0 untuk alasan kinerja, karena dalam hal ini panggilan dengan biaya hampir nol.
Matteo Italia