Saya sedang membaca utas berjudul "strlen vs sizeof" di CodeGuru , dan salah satu balasan menyatakan bahwa "lagian [praktik] buruk untuk menginisialisasi [sic] char
array dengan string literal."
Apakah ini benar, atau apakah itu hanya pendapatnya (walaupun "anggota elit")?
Ini pertanyaan aslinya:
#include <stdio.h>
#include<string.h>
main()
{
char string[] = "october";
strcpy(string, "september");
printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string));
return 0;
}
Baik. ukurannya harus panjang plus 1 ya?
ini adalah output
the size of september is 8 and the length is 9
Ukuran harus 10 pasti. itu seperti menghitung sizeof string sebelum diubah oleh strcpy tetapi panjang setelahnya.
Apakah ada yang salah dengan sintaks saya atau apa?
Inilah jawabannya :
Lagi pula itu praktik yang buruk untuk menginisialisasi array char dengan string literal. Jadi selalu lakukan salah satu dari yang berikut:
const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
c
programming-practices
strings
array
Cole Johnson
sumber
sumber
Jawaban:
Penulis komentar itu tidak pernah benar-benar membenarkannya, dan saya merasa pernyataan itu membingungkan.
Di C (dan Anda telah menandai ini sebagai C), itu satu-satunya cara untuk menginisialisasi array
char
dengan nilai string (inisialisasi berbeda dari penugasan). Anda bisa menulisatau
atau
Dalam kasus pertama, ukuran array diambil dari ukuran initializer. Literal string disimpan sebagai array
char
dengan terminasi 0 byte, sehingga ukuran array adalah 8 ('o', 'c', 't', 'o', 'b', 'e', 'r', 0). Dalam dua kasus kedua, ukuran array ditentukan sebagai bagian dari deklarasi (8 danMAX_MONTH_LENGTH
, apa pun yang terjadi).Yang tidak bisa Anda lakukan adalah menulis sesuatu
atau
dll Dalam kasus pertama, deklarasi
string
ini tidak lengkap karena tidak ada ukuran array telah ditetapkan dan tidak ada initializer untuk mengambil ukuran dari. Dalam kedua kasus,=
tidak akan bekerja karena a) ekspresi array sepertistring
mungkin bukan target penugasan dan b)=
operator tidak didefinisikan untuk menyalin konten dari satu array ke array yang lain.Dengan token yang sama, Anda tidak dapat menulis
di mana
foo
array lain darichar
. Bentuk inisialisasi ini hanya akan bekerja dengan string literal.SUNTING
Saya harus mengubah ini untuk mengatakan bahwa Anda juga dapat menginisialisasi array untuk menahan string dengan penginisialisasi gaya array, seperti
atau
tetapi lebih mudah di mata untuk menggunakan string literal.
EDIT 2
Untuk menetapkan konten array di luar deklarasi, Anda harus menggunakan
strcpy/strncpy
(untuk string yang diakhiri 0) ataumemcpy
(untuk semua jenis array lainnya):atau
sumber
strncpy
jarang merupakan jawaban yang tepatchar[8] str = "october";
adalah praktik buruk. Saya benar-benar harus menghitung sendiri untuk memastikan itu bukan overflow dan rusak dalam pemeliharaan ... misalnya memperbaiki kesalahan ejaan dariseprate
keseparate
akan rusak jika ukuran tidak diperbarui.strlen()
tidak termasuk karakter nol, gunakanMAX_MONTH_LENGTH
untuk menahan ukuran maksimum yang diperlukan agarchar string[]
sering terlihat salah. IMO,MAX_MONTH_SIZE
akan lebih baik di sini.Satu-satunya masalah yang saya ingat adalah menetapkan string literal ke
char *
:Sebagai contoh, ambil program ini:
Ini pada platform saya (Linux) macet karena mencoba untuk menulis ke halaman yang ditandai sebagai hanya-baca. Pada platform lain mungkin mencetak 'September' dll.
Yang mengatakan - inisialisasi secara literal membuat jumlah pemesanan tertentu sehingga ini tidak akan berfungsi:
Tapi ini akan
Sebagai komentar terakhir - saya tidak akan menggunakan
strcpy
sama sekali:Sementara beberapa kompiler dapat mengubahnya menjadi panggilan
strncpy
aman jauh lebih aman:sumber
strncpy
karena itu tidak mengakhiri string yang disalin ketika panjangnyasomething_else
lebih besar darisizeof(buf)
. Saya biasanya mengatur karakter terakhirbuf[sizeof(buf)-1] = 0
untuk melindungi dari itu, atau jikabuf
nol diinisialisasi, gunakansizeof(buf) - 1
sebagai panjang salinan.strlcpy
ataustrcpy_s
atau bahkansnprintf
jika Anda harus.strlcpy
dansnprintf
tidak dapat diakses langsung di MSVC, setidaknya pesanan danstrcpy_s
tidak pada * nix).Satu hal yang tidak ditampilkan oleh kedua utas ini adalah:
vs.
Yang pertama akan melakukan sesuatu seperti:
Yang terakhir hanya melakukan memcpy. Standar C menegaskan bahwa jika ada bagian dari array yang diinisialisasi, semuanya adalah. Jadi dalam hal ini, lebih baik melakukannya sendiri. Saya pikir itulah yang mungkin terjadi pada treuss.
Tentunya
lebih baik daripada:
atau
ps Untuk poin bonus, Anda dapat melakukan:
untuk membuang waktu kompilasi bagi dengan kesalahan nol jika Anda hendak melimpahi array.
sumber
Terutama karena Anda tidak akan memiliki ukuran
char[]
dalam variabel / konstruksi yang dapat Anda gunakan dengan mudah dalam program.Contoh kode dari tautan:
string
dialokasikan pada tumpukan sepanjang 7 atau 8 karakter. Saya tidak ingat apakah itu diakhiri dengan nol dengan cara ini atau tidak - utas yang Anda tautkan menyatakan bahwa itu adalah nol.Menyalin "september" pada string itu adalah memori yang jelas dibanjiri.
Tantangan lain muncul jika Anda beralih
string
ke fungsi lain sehingga fungsi lainnya dapat menulis ke dalam array. Anda perlu memberitahu fungsi lain berapa lama array begitu itu tidak membuat overrun. Anda bisa meneruskanstring
dengan hasilstrlen()
tetapi utas menjelaskan bagaimana ini bisa meledak jikastring
tidak diakhiri null.Anda lebih baik mengalokasikan string dengan ukuran tetap (lebih disukai didefinisikan sebagai konstanta) dan kemudian meneruskan array dan ukuran tetap ke fungsi lainnya. Komentar @John Bode benar, dan ada cara untuk mengurangi risiko ini. Mereka juga membutuhkan lebih banyak upaya dari Anda untuk menggunakannya.
Dalam pengalaman saya, nilai yang saya inisialisasi
char[]
ke biasanya terlalu kecil untuk nilai-nilai lain yang perlu saya tempatkan di sana. Menggunakan konstanta yang didefinisikan membantu menghindari masalah itu.sizeof string
akan memberi Anda ukuran buffer (8 byte); gunakan hasil ekspresi itu alih-alihstrlen
saat Anda khawatir tentang memori.Demikian pula, Anda dapat membuat cek sebelum panggilan untuk
strcpy
untuk melihat jika target buffer Anda cukup besar untuk string sumber:if (sizeof target > strlen(src)) { strcpy (target, src); }
.Ya, jika Anda harus melewati array ke fungsi, Anda harus lulus ukuran fisik juga:
foo (array, sizeof array / sizeof *array);
. - John Bodesumber
sizeof string
akan memberi Anda ukuran buffer (8 byte); gunakan hasil ekspresi itu alih-alihstrlen
saat Anda khawatir tentang memori. Demikian pula, Anda dapat membuat cek sebelum panggilan untukstrcpy
untuk melihat jika target buffer Anda cukup besar untuk string sumber:if (sizeof target > strlen(src)) { strcpy (target, src); }
. Ya, jika Anda harus melewati array ke fungsi, Anda harus lulus ukuran fisik juga:foo (array, sizeof array / sizeof *array);
.string
menghasilkan konversi implisit kechar*
, menunjuk ke elemen pertama array. Ini kehilangan informasi batas array. Panggilan fungsi hanyalah salah satu dari banyak konteks di mana ini terjadi.char *ptr = string;
adalah yang lain. Bahkanstring[0]
adalah contoh dari ini; yang[]
Operator bekerja pada pointer, tidak langsung pada array. Disarankan membaca: Bagian 6 dari comp.lang.c FAQ .Saya pikir ide "praktik buruk" berasal dari kenyataan bahwa bentuk ini:
secara implisit membuat strcpy dari kode mesin sumber ke stack.
Lebih efisien untuk hanya menangani tautan ke string itu. Suka dengan:
atau langsung:
(tapi tentu saja di sebagian besar kode mungkin tidak masalah)
sumber
char time_buf[] = "00:00";
mana Anda akan memodifikasi buffer? Achar *
diinisialisasi ke string literal diatur ke alamat byte pertama, jadi mencoba untuk memodifikasinya menghasilkan perilaku yang tidak terdefinisi karena metode penyimpanan string literal tidak diketahui (implementasi didefinisikan), sementara memodifikasi byte achar[]
adalah legal karena inisialisasi menyalin byte ke ruang tulis yang dialokasikan pada stack. Untuk mengatakan bahwa itu "kurang efisien" atau "praktik buruk" tanpa menguraikan nuansachar* vs char[]
yang menyesatkan.Tidak pernah terlalu lama, tetapi Anda harus menghindari inisialisasi char [] ke string, karena, "string" adalah const char *, dan Anda menugaskannya ke char *. Jadi, jika Anda meneruskan karakter ini [] ke metode yang mengubah data, Anda dapat memiliki perilaku yang menarik.
Seperti yang saya katakan, saya mencampur sedikit char [] dengan char *, itu tidak bagus karena mereka sedikit berbeda.
Tidak ada yang salah tentang menugaskan data ke array char, tetapi karena niat menggunakan array ini adalah menggunakannya sebagai 'string' (char *), mudah untuk melupakan bahwa Anda tidak boleh memodifikasi array ini.
sumber
const
kecuali Anda mendefinisikannya seperti itu. (Dan string literal dalam C tidakconst
, meskipun setiap upaya untuk memodifikasi string literal memang memiliki perilaku yang tidak terdefinisi.)char *s = "literal";
Memang memiliki jenis perilaku yang Anda bicarakan; lebih baik ditulis sebagaiconst char *s = "literal";
const
onint n = 42;
, karena42
konstan.c
dapat dimodifikasi. Ini adalah jaminan yang sama kuatnya dengan yang1 + 1
dievaluasi2
. Jika program yang saya tautkan di atas melakukan apa pun selain mencetakEFGH
, ini menunjukkan implementasi C yang tidak sesuai.