Ini kode saya:
#include <string.h>
#include <stdio.h>
typedef char BUF[8];
typedef struct
{
BUF b[23];
} S;
S s;
int main()
{
int n;
memcpy(&s, "1234567812345678", 17);
n = strlen((char *)&s.b) / sizeof(BUF);
printf("%d\n", n);
n = strlen((char *)&s) / sizeof(BUF);
printf("%d\n", n);
}
Menggunakan gcc 8.3.0 atau 8.2.1 dengan level optimasi apa pun kecuali -O0
, ini akan keluar 0 2
ketika saya mengharapkan 2 2
. Kompilator memutuskan bahwa strlen
terikat b[0]
dan karenanya tidak pernah sama atau melebihi nilai yang dibagi.
Apakah ini bug di kode saya atau bug di compiler?
Ini tidak dijabarkan dalam standar dengan jelas, tetapi saya pikir interpretasi arus utama dari sumber pointer adalah bahwa untuk objek apa pun X
, kode tersebut (char *)&X
harus menghasilkan pointer yang dapat beralih ke seluruh X
- konsep ini harus berlaku bahkan jika X
kebetulan memiliki sub-array sebagai struktur internal.
(Pertanyaan bonus, apakah ada tanda gcc untuk mematikan pengoptimalan khusus ini?)
2 2
bawah berbagai opsi.s.b
terbatas untukb[0]
itu terbatas pada 8 karakter, dan karenanya dua opsi: (1) akses keluar jika ada 8 karakter non-nol, yaitu UB, (2) ada karakter nol, di mana len kurang dari 8, maka membaginya dengan 8 memberi nol. Jadi menyusun (1) + (2) kompiler dapat menggunakan UB untuk memberikan hasil yang sama untuk kedua kasusJawaban:
Ada beberapa masalah yang bisa saya lihat dan mereka dapat dipengaruhi oleh bagaimana kompiler memutuskan untuk tata letak memori.
Dalam kode di atas
s.b
adalah array entri 23 array 8 karakter. Ketika Anda merujuk hanyas.b
Anda mendapatkan alamat entri pertama dalam array 23 byte (dan byte pertama dalam array 8 karakter). Ketika kode mengatakan&s.b
, ini meminta alamat dari array. Di bawah penutup, kompiler kemungkinan besar menghasilkan beberapa penyimpanan lokal, menyimpan alamat array di sana dan memasok alamat penyimpanan lokal kestrlen
.Anda memiliki 2 solusi yang memungkinkan. Mereka:
atau
Saya juga mencoba untuk menjalankan program Anda dan menunjukkan masalah, tetapi baik dentang dan versi gcc yang saya miliki dengan
-O
opsi apa pun masih berfungsi seperti yang Anda harapkan. Untuk apa nilainya, saya menjalankan dentang versi 9.0.0-2 dan gcc versi 9.2.1 di x86_64-pc-linux-gnu).sumber
Ada kesalahan dalam kode.
misalnya, berisiko, meskipun s dimulai dengan b harus:
Strlen kedua () juga memiliki kesalahan
misalnya, harus:
String sb, jika disalin dengan benar, harus sepanjang 17 huruf. Tidak yakin bagaimana struct disimpan dalam memori, jika disejajarkan. Sudahkah Anda memeriksa bahwa sb sebenarnya mengandung 17 karakter yang disalin?
Jadi strlen (sb) harus menunjukkan 17
Printf hanya menunjukkan angka integer, karena% d adalah integer, dan variabel n dinyatakan sebagai integer. sizeof (BUF), harus 8
Jadi 17 dibagi 8 (17/8) harus mencetak 2 karena n dinyatakan sebagai bilangan bulat. Karena memcpy digunakan untuk menyalin data ke s dan bukan ke sb, saya kira karena ini berkaitan dengan penyelarasan memori; dengan asumsi itu adalah komputer 64 bit, maka dapat ada 8 karakter pada satu alamat memori.
Misalnya, mari kita asumsikan bahwa seseorang telah memanggil malloc (1), daripada "ruang kosong" berikutnya tidak selaras ...
Panggilan strlen kedua, menunjukkan nomor yang benar, ketika salinan string dilakukan ke s struct bukan ke sb
sumber