Saya dapat melihat dua 'some'
literal dalam kode assembler yang dihasilkan oleh MSVC, tetapi hanya satu dengan clang dan gcc. Ini mengarah pada hasil eksekusi kode yang sangat berbeda.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Adakah yang bisa menjelaskan perbedaan dan persamaan antara hasil kompilasi tersebut? Mengapa clang / gcc mengoptimalkan sesuatu meskipun tidak ada pengoptimalan yang diminta? Apakah ini semacam perilaku yang tidak terdefinisi?
Saya juga memperhatikan bahwa jika saya mengubah deklarasi seperti yang ditunjukkan di bawah ini, clang / gcc / msvc tidak meninggalkan "some"
kode assembler sama sekali. Mengapa perilakunya berbeda?
static const char A[] = "some";
static const char B[] = "some";
c++
language-lawyer
string-literals
string-interning
Eugene Kosov
sumber
sumber
Jawaban:
Ini bukanlah perilaku yang tidak ditentukan, tetapi perilaku yang tidak ditentukan. Untuk literal string ,
Itu berarti hasil dari
A == B
mungkintrue
ataufalse
, yang seharusnya tidak Anda andalkan.Dari standar, [lex.string] / 16 :
sumber
Jawaban lain menjelaskan mengapa Anda tidak dapat mengharapkan alamat penunjuk berbeda. Namun Anda dapat dengan mudah menulis ulang ini dengan cara yang menjamin itu
A
danB
tidak sebanding:static const char A[] = "same"; static const char B[] = "same";// but different void f() { if (A == B) { throw "Hello, string merging!"; } }
Perbedaannya adalah itu
A
danB
sekarang adalah array karakter. Ini berarti bahwa mereka bukan pointer dan alamat mereka harus berbeda seperti dua variabel integer yang seharusnya. C ++ membingungkan ini karena membuat pointer dan array tampak dapat dipertukarkan (operator*
danoperator[]
tampaknya berperilaku sama), tetapi keduanya sangat berbeda. Misalnya sesuatu seperticonst char *A = "foo"; A++;
itu legal, tetapiconst char A[] = "bar"; A++;
tidak.Salah satu cara untuk memikirkan perbedaannya adalah dengan
char A[] = "..."
mengatakan "beri saya blok memori dan isi dengan karakter...
diikuti oleh\0
", sedangkanchar *A= "..."
mengatakan "beri saya alamat di mana saya dapat menemukan karakter...
diikuti oleh\0
".sumber
*p
danp[0]
tidak hanya "tampaknya berperilaku sama" tetapi menurut definisi adalah identik (asalkan itup+0 == p
adalah hubungan identitas karena0
merupakan elemen netral dalam penambahan pointer-integer). Bagaimanapun,p[i]
didefinisikan sebagai*(p+i)
. Jawabannya membuat poin yang bagus.typeof(*p)
dantypeof(p[0])
keduanyachar
jadi tidak banyak lagi yang bisa berbeda. Saya setuju bahwa 'tampaknya berperilaku sama' bukanlah kata-kata yang terbaik, karena semantiknya sangat berbeda. Posting Anda mengingatkan saya cara terbaik untuk elemen akses C ++ array:0[p]
,1[p]
,2[p]
dll Ini adalah bagaimana pro melakukannya, setidaknya ketika mereka ingin membingungkan orang yang lahir setelah bahasa pemrograman C.Apakah compiler memilih untuk menggunakan lokasi string yang sama untuk
A
danB
tergantung pada implementasinya. Secara formal, Anda dapat mengatakan bahwa perilaku kode Anda tidak ditentukan .Kedua pilihan tersebut mengimplementasikan standar C ++ dengan benar.
sumber
Ini adalah pengoptimalan untuk menghemat ruang, sering disebut "penggabungan string". Berikut adalah dokumen untuk MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Oleh karena itu, jika Anda menambahkan / GF ke baris perintah, Anda akan melihat perilaku yang sama dengan MSVC.
Ngomong-ngomong, Anda mungkin tidak boleh membandingkan string melalui pointer seperti itu, alat analisis statis apa pun yang layak akan menandai kode itu sebagai rusak. Anda perlu membandingkan apa yang mereka tunjuk, bukan nilai penunjuk yang sebenarnya.
sumber