Pertanyaan Apakah memcpy (& a + 1, & b + 1, 0) didefinisikan dalam C11?


Pertanyaan ini mengikuti ini pertanyaan sebelumnya tentang definisi dari memcpy(0, 0, 0), yang telah secara pasti ditentukan untuk menjadi perilaku yang tidak terdefinisi.

Seperti yang ditunjukkan oleh pertanyaan terkait, jawabannya bergantung pada isi klausa C11 7.1.4: 1

Setiap pernyataan berikut berlaku kecuali dinyatakan secara eksplisit sebaliknya dalam uraian rinci yang mengikuti: Jika argumen ke fungsi memiliki nilai yang tidak valid (seperti nilai di luar domain fungsi, atau penunjuk di luar ruang alamat program, atau penunjuk nol, [...]) [...] perilaku tidak terdefinisi. [...]

Fungsi standar memcpy() mengharapkan petunjuk ke void dan const void, seperti:

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

Pertanyaannya patut ditanyakan sama sekali hanya karena ada dua pengertian dari pointer "valid" dalam standar: ada pointer yang secara sah dapat diperoleh melalui aritmatika pointer dan secara sah dapat dibandingkan dengan <, > ke pointer lain di dalam objek yang sama. Dan ada pointer yang valid untuk dereferencing. Kelas sebelumnya mencakup pointer "satu-terakhir" seperti &a + 1 dan &b + 1 di cuplikan berikut, sedangkan kelas terakhir tidak memasukkan ini sebagai valid.

char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);

Jika potongan di atas dianggap perilaku yang ditentukan, mengingat fakta bahwa argumen memcpy() diketik sebagai pointer ke void bagaimanapun juga, jadi pertanyaan validitasnya masing-masing tidak bisa mengenai dereferencing mereka. Atau seharusnya &a + 1 dan &b + 1 dianggap "di luar ruang alamat program"?

Ini penting bagi saya karena saya sedang dalam proses memformalkan efek fungsi standar C. Saya telah menulis satu pra-kondisi memcpy() sebagai requires \valid(s1+(0 .. n-1));, sampai itu menunjukkan perhatian saya bahwa GCC 4.9 sudah mulai secara agresif mengoptimalkan fungsi perpustakaan seperti panggilan melampaui apa yang diungkapkan dalam rumus di atas (memang). Rumusnya \valid(s1+(0 .. n-1)) dalam bahasa spesifikasi khusus ini setara dengan true kapan n aku s 0, dan tidak menangkap perilaku tidak terdefinisi yang tergantung pada GCC 4.9 untuk mengoptimalkan. 


32
2017-08-19 18:33


asal


Jawaban:


C11 mengatakan:

(C11, 7.24.2.1p2) "Fungsi memcpy menyalin n karakter dari objek yang ditunjuk oleh s2 ke objek yang ditunjuk oleh s1."

&a + 1 sendiri adalah pointer yang valid untuk penambahan bilangan bulat tetapi &a + 1 bukan penunjuk ke suatu objek, sehingga panggilan tersebut memanggil perilaku tidak terdefinisi.


18
2017-08-19 18:39



Sementara jawaban "benar" menurut standar tampaknya tidak setuju, saya dapat menemukannya hanya tidak jujur ​​bahwa setelah int [6]; int b [6]; semua

memcpy(a+0, b+0, 6);
memcpy(a+1, b+1, 5);
memcpy(a+2, b+2, 4);
memcpy(a+3, b+3, 3);
memcpy(a+4, b+4, 2);
memcpy(a+5, b+5, 1);

harus valid (dan salin area yang berakhir di ujung array) sementara

memcpy(a+6, b+6, 0);

valid dalam hal jumlah tetapi bukan alamat. Ini adalah ujung yang sama dari area yang disalin!

Secara pribadi, saya akan condong ke mendefinisikan memcpy (0,0,0) juga berlaku (dengan alasan hanya menuntut pointer yang valid tetapi tidak ada objek) tapi setidaknya itu adalah kasus tunggal sementara "akhir dari array" kasus adalah pengecualian aktual untuk pola biasa lain untuk menyalin area di ujung larik.


5
2017-08-19 21:47