Function call dengan pointer ke non-const dan pointer ke const const dari alamat yang sama
14
Saya ingin menulis fungsi yang menginput array data dan output array data lain menggunakan pointer.
Saya bertanya-tanya apa hasilnya jika keduanya srcdan dstmenunjuk ke alamat yang sama karena saya tahu kompiler dapat mengoptimalkan untuk const. Apakah itu perilaku yang tidak terdefinisi? (Saya menandai C dan C ++ karena saya tidak yakin apakah jawabannya mungkin berbeda di antara mereka, dan saya ingin tahu tentang keduanya.)
Meskipun benar bahwa perilaku didefinisikan dengan baik - tidak benar bahwa kompiler dapat "mengoptimalkan konst" dalam arti yang Anda maksud.
Artinya, sebuah kompiler tidak diperbolehkan menganggap bahwa hanya karena parameternya adalah const T* ptr, memori yang ditunjuk oleh ptrtidak akan diubah melalui pointer lain. Pointer bahkan tidak harus sama. Ini constadalah kewajiban, bukan jaminan - kewajiban Anda (= fungsi) untuk tidak melakukan perubahan melalui pointer itu.
Untuk benar-benar memiliki jaminan itu, Anda harus menandai penunjuk dengan restrictkata kunci. Jadi, jika Anda mengkompilasi dua fungsi ini:
int foo(constint* x,int* y){int result =*x;(*y)++;return result +*x;}int bar(constint* x,int*restrict y){int result =*x;(*y)++;return result +*x;}
yang foo()fungsi harus membaca dua kali dari x, sementara bar()hanya perlu membaca sekali:
foo:
mov eax, DWORD PTR [rdi]add DWORD PTR [rsi],1add eax, DWORD PTR [rdi]# second read
ret
bar:
mov eax, DWORD PTR [rdi]add DWORD PTR [rsi],1add eax, eax # no second read
ret
restricthanya kata kunci dalam C (sejak C99); sayangnya, belum diperkenalkan ke C ++ sejauh ini (untuk alasan yang buruk yang lebih rumit untuk memperkenalkannya di C ++). Namun, banyak kompiler yang mendukungnya __restrict.
Intinya: Kompiler harus mendukung use case "esoterik" Anda saat kompilasi f(), dan tidak akan memiliki masalah dengannya.
Lihat posting ini tentang kasus penggunaan untuk restrict.
constbukan "kewajiban Anda (= fungsi) untuk tidak melakukan perubahan melalui pointer itu". Standar C memungkinkan fungsi untuk menghapus constmelalui gips dan kemudian memodifikasi objek melalui hasilnya. Pada dasarnya, constini hanyalah saran dan kemudahan bagi programmer untuk membantu menghindari memodifikasi objek secara tidak sengaja.
Eric Postpischil
@EricPostpischil: Ini kewajiban yang bisa Anda dapatkan.
einpoklum
Kewajiban yang bisa Anda dapatkan bukanlah kewajiban.
Eric Postpischil
2
@EricPostpischil: 1. Rambut Anda membelah di sini. 2. Itu tidak benar.
einpoklum
1
Inilah sebabnya memcpydan strcpydideklarasikan dengan restrictargumen, sementara memmovetidak - hanya yang terakhir memungkinkan tumpang tindih antara blok memori.
Barmar
5
Ini didefinisikan dengan baik (dalam C ++, tidak yakin dalam C lagi), dengan dan tanpa constkualifikasi.
Hal pertama yang harus dicari adalah aturan aliasing yang ketat 1 . Jika srcdan dstmenunjuk ke objek yang sama:
di C, mereka harus dari tipe yang kompatibel ; char*dan char const*tidak kompatibel.
di C ++, mereka harus dari tipe yang serupa ; char*dan char const*serupa.
Mengenai constkualifikasi, Anda mungkin berpendapat bahwa karena ketika dst == srcfungsi Anda secara efektif mengubah srcpoin apa , srctidak boleh dikualifikasikan sebagai const. Ini bukan cara constkerjanya. Dua kasus perlu dipertimbangkan:
Ketika suatu objek didefinisikan sebagai const, seperti dalam char const data[42];, memodifikasinya (secara langsung atau tidak langsung) mengarah ke Perilaku Tidak Terdefinisi.
Ketika referensi atau penunjuk ke constobjek didefinisikan, seperti dalam char const* pdata = data;, seseorang dapat memodifikasi objek yang mendasarinya asalkan belum didefinisikan sebagai const2 (lihat 1.). Jadi yang berikut ini didefinisikan dengan baik:
int main(){int result =42;intconst* presult =&result;*const_cast<int*>(presult)=0;return*presult;// 0}
Ungkapan "Ketika referensi atau pointer ke constobjek didefinisikan" salah. Anda berarti bahwa ketika referensi atau penunjuk ke tipe-const kualifikasi didefinisikan, itu tidak berarti objek yang diatur ke titik tidak dapat dimodifikasi (dengan berbagai cara). (Jika pointer menunjuk ke suatu objek, itu berarti objek tersebut memang berdasarkan definisi, sehingga perilaku mencoba memodifikasinya tidak didefinisikan.)constconst
Eric Postpischil
@ Eric, saya hanya spesifik ketika pertanyaannya tentang Standar atau ditandai language-lawyer. Exactitude adalah nilai yang saya hargai, tetapi saya juga sadar itu datang dengan lebih banyak kompleksitas. Di sini, saya memutuskan untuk memilih kalimat yang sederhana dan mudah dipahami, karena saya percaya ini adalah keinginan OP. Jika Anda berpikir sebaliknya, silakan jawab, saya akan menjadi orang pertama yang mengangkatnya. Bagaimanapun, terima kasih atas komentar Anda.
YSC
3
Ini didefinisikan dengan baik dalam C. Aturan aliasing yang ketat tidak berlaku dengan chartipe, atau dengan dua pointer dari tipe yang sama.
Saya tidak yakin apa yang Anda maksud dengan "optimalkan untuk const". Kompiler saya (GCC 8.3.0 x86-64) menghasilkan kode yang sama persis untuk kedua kasus. Jika Anda menambahkan restrictspecifier ke pointer, maka kode yang dihasilkan sedikit lebih baik, tetapi itu tidak akan berfungsi untuk kasus Anda, pointer menjadi sama.
(C11 §6.5 7)
Objek harus memiliki nilai yang disimpan diakses hanya oleh ekspresi lvalue yang memiliki salah satu dari jenis berikut:
- jenis yang kompatibel dengan jenis objek yang efektif,
- versi yang memenuhi syarat dari jenis yang kompatibel dengan jenis objek yang efektif,
- tipe yang tipe bertanda tangan atau tidak bertanda yang sesuai dengan jenis objek yang efektif,
- jenis tipe yang bertanda tangan atau tidak bertanda yang sesuai dengan versi terkualifikasi dari jenis objek yang efektif,
- tipe agregat atau gabungan yang mencakup satu dari jenis-jenis yang disebutkan di atas di antara para anggotanya (termasuk, secara rekursif, anggota dari sub-agregat atau kesatuan yang terkandung), atau
- jenis karakter.
Dalam hal ini (tanpa restrict), Anda akan selalu mendapatkan 121hasilnya.
const
bukan "kewajiban Anda (= fungsi) untuk tidak melakukan perubahan melalui pointer itu". Standar C memungkinkan fungsi untuk menghapusconst
melalui gips dan kemudian memodifikasi objek melalui hasilnya. Pada dasarnya,const
ini hanyalah saran dan kemudahan bagi programmer untuk membantu menghindari memodifikasi objek secara tidak sengaja.memcpy
danstrcpy
dideklarasikan denganrestrict
argumen, sementaramemmove
tidak - hanya yang terakhir memungkinkan tumpang tindih antara blok memori.Ini didefinisikan dengan baik (dalam C ++, tidak yakin dalam C lagi), dengan dan tanpa
const
kualifikasi.Hal pertama yang harus dicari adalah aturan aliasing yang ketat 1 . Jika
src
dandst
menunjuk ke objek yang sama:char*
danchar const*
tidak kompatibel.char*
danchar const*
serupa.Mengenai
const
kualifikasi, Anda mungkin berpendapat bahwa karena ketikadst == src
fungsi Anda secara efektif mengubahsrc
poin apa ,src
tidak boleh dikualifikasikan sebagaiconst
. Ini bukan caraconst
kerjanya. Dua kasus perlu dipertimbangkan:const
, seperti dalamchar const data[42];
, memodifikasinya (secara langsung atau tidak langsung) mengarah ke Perilaku Tidak Terdefinisi.const
objek didefinisikan, seperti dalamchar const* pdata = data;
, seseorang dapat memodifikasi objek yang mendasarinya asalkan belum didefinisikan sebagaiconst
2 (lihat 1.). Jadi yang berikut ini didefinisikan dengan baik:1) Apa aturan aliasing yang ketat?
2) Apakah
const_cast
aman?sumber
char*
danchar const*
tidak kompatibel._Generic((char *) 0, const char *: 1, default: 0))
mengevaluasi ke nol.const
objek didefinisikan" salah. Anda berarti bahwa ketika referensi atau penunjuk ke tipe-const
kualifikasi didefinisikan, itu tidak berarti objek yang diatur ke titik tidak dapat dimodifikasi (dengan berbagai cara). (Jika pointer menunjuk ke suatu objek, itu berarti objek tersebut memang berdasarkan definisi, sehingga perilaku mencoba memodifikasinya tidak didefinisikan.)const
const
language-lawyer
. Exactitude adalah nilai yang saya hargai, tetapi saya juga sadar itu datang dengan lebih banyak kompleksitas. Di sini, saya memutuskan untuk memilih kalimat yang sederhana dan mudah dipahami, karena saya percaya ini adalah keinginan OP. Jika Anda berpikir sebaliknya, silakan jawab, saya akan menjadi orang pertama yang mengangkatnya. Bagaimanapun, terima kasih atas komentar Anda.Ini didefinisikan dengan baik dalam C. Aturan aliasing yang ketat tidak berlaku dengan
char
tipe, atau dengan dua pointer dari tipe yang sama.Saya tidak yakin apa yang Anda maksud dengan "optimalkan untuk
const
". Kompiler saya (GCC 8.3.0 x86-64) menghasilkan kode yang sama persis untuk kedua kasus. Jika Anda menambahkanrestrict
specifier ke pointer, maka kode yang dihasilkan sedikit lebih baik, tetapi itu tidak akan berfungsi untuk kasus Anda, pointer menjadi sama.(C11 §6.5 7)
Dalam hal ini (tanpa
restrict
), Anda akan selalu mendapatkan121
hasilnya.sumber