Apakah string C selalu dibatalkan nol, atau apakah itu tergantung pada platform?

13

Saat ini saya sedang bekerja dengan sistem tertanam dan mencari cara untuk menerapkan string pada mikroprosesor tanpa sistem operasi. Sejauh ini apa yang saya lakukan hanya menggunakan ide untuk memiliki NULL diakhiri dengan pointer karakter dan memperlakukan mereka sebagai string di mana NULL menandakan akhir. Saya tahu ini cukup umum, tetapi bisakah Anda selalu mengandalkan ini?

Alasan saya bertanya adalah karena saya berpikir mungkin menggunakan sistem operasi waktu nyata, dan saya ingin menggunakan kembali sebanyak mungkin kode saya saat ini. Jadi untuk berbagai pilihan yang ada di luar sana, dapatkah saya berharap string bekerja dengan baik?

Biarkan saya lebih spesifik untuk kasus saya. Saya menerapkan sistem yang mengambil dan memproses perintah melalui port serial. Bisakah saya menjaga kode pemrosesan perintah saya sama, dan kemudian berharap bahwa objek string yang dibuat pada RTOS (yang berisi perintah) untuk semua akan dihentikan NULL? Atau, apakah akan berbeda berdasarkan OS?

Memperbarui

Setelah disarankan untuk melihat pertanyaan ini, saya telah menentukan bahwa itu tidak menjawab apa yang saya tanyakan. Pertanyaannya sendiri adalah menanyakan apakah panjang string harus selalu dilewati yang sama sekali berbeda dari apa yang saya tanyakan, dan meskipun beberapa jawaban memiliki informasi berguna di dalamnya, mereka tidak persis seperti yang saya cari. Jawaban di sana sepertinya memberikan alasan mengapa atau mengapa tidak mengakhiri string dengan karakter nol. Perbedaan dengan apa yang saya tanyakan adalah apakah saya dapat lebih atau kurang mengharapkan string bawaan dari platform yang berbeda untuk mengakhiri string mereka sendiri dengan nol, tanpa harus keluar dan mencoba setiap platform di luar sana jika itu masuk akal.

Mengintip
sumber
3
Saya belum pernah menggunakan C dalam waktu yang lama, tetapi saya tidak bisa memikirkan saat ketika saya mengalami implementasi yang tidak menggunakan string yang diakhiri NULL. Itu adalah bagian dari standar C, jika saya ingat dengan benar (seperti yang saya katakan, sudah lama ...)
MetalMikester
1
Saya bukan spesialis dalam C, tetapi sejauh yang saya tahu semua string dalam C adalah array dari char, null-terminated. Anda dapat membuat tipe string Anda sendiri, tetapi Anda harus menerapkan sendiri semua fungsi manipulasi string.
Machado
1
@MetalMikester Anda berpikir bahwa informasi ini dapat ditemukan dalam spesifikasi C standar?
Snoop
3
@ Salin Kemungkinan besar, ya. Tapi sungguh, ketika berbicara tentang string dalam C, mereka hanya sebuah array karakter yang diakhiri dengan NULL dan hanya itu, kecuali jika Anda menggunakan semacam pustaka string non-standar, tapi bukan itu yang sedang kita bicarakan di sini. Saya ragu Anda akan menemukan platform yang tidak menghargai itu, terutama dengan salah satu kekuatan C adalah portabilitas.
MetalMikester

Jawaban:

42

Hal-hal yang disebut "string C" akan diakhiri null pada platform apa pun. Begitulah fungsi perpustakaan C standar menentukan akhir string.

Dalam bahasa C, tidak ada yang menghentikan Anda dari memiliki array karakter yang tidak berakhir dengan nol. Namun Anda harus menggunakan beberapa metode lain untuk menghindari kehabisan string.

Simon B
sumber
4
hanya untuk menambahkan; biasanya Anda memiliki bilangan bulat di suatu tempat untuk melacak panjang string dan kemudian Anda berakhir dengan struktur data kustom untuk melakukannya dengan benar, sesuatu seperti kelas QString di Qt
Rudolf Olah
8
Contoh kasus: Saya bekerja dengan program C yang menggunakan setidaknya lima format string yang berbeda: chararray yang diakhiri null , chararray dengan panjang yang disandikan dalam byte pertama (umumnya dikenal sebagai "string Pascal"), wchar_tversi berbasis dari kedua di atas, dan chararray yang menggabungkan kedua metode: panjang dikodekan dalam byte pertama, dan karakter nol mengakhiri string.
Mark
4
@Mark Interfacing dengan banyak komponen / aplikasi pihak ke-3 atau kekacauan kode lama?
Dan Is Fiddling By Firelight
2
@DanNeely, semua yang di atas. String pascal untuk berinteraksi dengan MacOS klasik, string C untuk penggunaan internal dan Windows, string lebar untuk menambahkan dukungan Unicode, dan string bajingan karena seseorang mencoba menjadi pintar dan membuat string yang dapat berinteraksi dengan MacOS dan Windows pada saat yang sama.
Mark
1
@ Markus ... dan tentu saja tidak ada yang mau menghabiskan uang untuk melunasi hutang teknis karena MacOS klasik sudah lama mati, dan string bajingan adalah clusterfrak ganda setiap kali mereka perlu disentuh. Simpati saya.
Dan Is Fiddling By Firelight
22

Penentuan karakter terminating tergantung pada kompiler untuk literal dan implementasi pustaka standar untuk string secara umum. Itu tidak ditentukan oleh sistem operasi.

Konvensi NULpenghentian kembali ke pra-standar C, dan dalam 30+ tahun, saya tidak bisa mengatakan saya mengalami lingkungan yang melakukan hal lain. Perilaku ini dikodifikasikan dalam C89 dan terus menjadi bagian dari standar bahasa C (tautan ke konsep C99):

  • Bagian 6.4.5 mengatur tahap untuk NULstring-diminminasikan dengan mengharuskan agar NULditambahkan ke string literal.
  • Bagian 7.1.1 membawanya ke fungsi-fungsi di perpustakaan standar dengan mendefinisikan string sebagai "urutan karakter yang berdekatan yang diakhiri oleh dan termasuk karakter nol pertama."

Tidak ada alasan mengapa seseorang tidak dapat menulis fungsi yang menangani string yang diakhiri oleh beberapa karakter lain, tetapi juga tidak ada alasan untuk melawan standar yang ditetapkan dalam banyak kasus kecuali tujuan Anda adalah membuat programer cocok. :-)

Blrfl
sumber
2
Salah satu alasannya adalah untuk menghindari keharusan menemukan ujung senar yang sama berulang kali.
Paŭlo Ebermann
@ PaŭloEbermann Benar. Dengan mengorbankan harus melewati dua nilai, bukan satu. Yang agak menjengkelkan jika Anda hanya melewatkan string literal seperti pada printf("string: \"%s\"\n", "my cool string"). Satu-satunya cara melewati empat parameter dalam kasus ini (selain beberapa jenis terminasi byte) adalah dengan mendefinisikan string menjadi sesuatu seperti std::stringdi C ++, yang memiliki masalah dan keterbatasannya sendiri.
cmaster - mengembalikan monica
1
Bagian 6.4.5 tidak memerlukan sebuah literal string yang harus diakhiri dengan karakter null. Ini secara eksplisit mencatat " Sebuah string karakter literal tidak perlu menjadi string (lihat 7.1.1), karena karakter nol dapat tertanam di dalamnya dengan urutan pelarian \ 0. "
bzeaman
1
@bzeaman Catatan kaki mengatakan Anda dapat membuat string literal yang tidak memenuhi definisi 7.1.1 dari string, tetapi kalimat yang merujuk kepadanya mengatakan compiler compliant NUL-minimalkan mereka tidak peduli apa pun: "Dalam fase terjemahan 7, byte atau kode dari nilai nol ditambahkan ke setiap urutan karakter multibyte yang dihasilkan dari string literal atau literal. " Fungsi perpustakaan menggunakan definisi 7.1.1 berhenti pada saat pertama NULmereka menemukan dan tidak akan tahu atau peduli bahwa ada karakter tambahan di luarnya.
Blrfl
Saya berdiri dikoreksi. Saya mencari berbagai istilah seperti 'nol' tetapi ketinggalan 6.4.5.5 menyebutkan 'nilai nol'.
bzeaman
3

Saya bekerja dengan sistem tertanam ... tanpa sistem operasi ... Saya ... menggunakan gagasan memiliki pointer karakter NULL dihentikan dan memperlakukan mereka sebagai string di mana NULL menandakan akhir. Saya tahu ini cukup umum, tetapi bisakah Anda selalu mengandalkan ini?

Tidak ada tipe data string dalam bahasa C, tetapi ada string literal .

Jika Anda meletakkan string literal di program Anda, biasanya NUL akan dihentikan (tetapi lihat kasus khusus, dibahas dalam komentar di bawah ini.) Artinya, Jika Anda meletakkan "foobar"di tempat di mana const char *nilai diharapkan, kompiler akan memancarkan foobar⊘ke segmen const / kode / bagian dari program Anda, dan nilai ekspresi akan menjadi penunjuk ke alamat tempat ia menyimpan fkarakter. (Catatan: Saya menggunakan untuk menandakan byte NUL.)

Satu-satunya pengertian lain di mana bahasa C memiliki string adalah, ia memiliki beberapa pustaka rutin standar yang beroperasi pada urutan karakter yang diakhiri NUL. Rutinitas pustaka tersebut tidak akan ada di lingkungan bare metal kecuali Anda porting sendiri.

Itu hanya kode --- tidak berbeda dengan kode yang Anda tulis sendiri. Jika Anda tidak merusaknya saat Anda porting, maka mereka akan melakukan apa yang selalu mereka lakukan (misalnya, berhenti pada NUL.)

Solomon Lambat
sumber
2
Re: "Jika Anda meletakkan string literal dalam program Anda, itu akan selalu dihentikan NUL": Apakah Anda yakin tentang itu? Saya cukup yakin bahwa (misalnya) char foo[4] = "abcd";adalah cara yang valid untuk membuat array non-null-dihentikan empat karakter.
ruakh
2
@ruakh, Ups! itu adalah kasus yang tidak saya pertimbangkan. Saya sedang memikirkan string literal yang muncul di tempat char const * ekspresi yang diharapkan. Saya lupa bahwa inisialisasi C terkadang dapat mematuhi aturan yang berbeda.
Solomon Slow
@ruakh String literal dihentikan NUL. Array tidak.
jamesdlin
2
@ruakh Anda punya char[4]. Itu bukan string, tetapi ini diinisialisasi dari satu
Caleth
2
@ Caleth, "diinisialisasi dari satu" bukanlah sesuatu yang harus terjadi pada saat run time. Jika kita menambahkan kata kunci staticke contoh Ruakh, maka kompiler dapat memancarkan "abcd" non NUL ke segmen data yang diinisialisasi sehingga variabel diinisialisasi oleh pemuat program. Jadi, Ruakh benar: Setidaknya ada satu kasus di mana penampilan string literal dalam suatu program tidak mengharuskan kompiler untuk memancarkan string yang diakhiri NUL. (ps, saya benar-benar mengkompilasi contoh dengan gcc 5.4.0, dan kompiler tidak memancarkan NUL.)
Solomon Slow
2

Seperti yang telah disebutkan orang lain, null terminating string adalah konvensi dari C Standard Library. Anda dapat menangani string dengan cara apa pun yang Anda inginkan jika Anda tidak akan menggunakan perpustakaan standar.

Ini berlaku untuk semua sistem operasi dengan kompiler 'C', dan juga, Anda dapat menulis program 'C' yang tidak berjalan di bawah sistem operasi yang benar seperti yang Anda sebutkan dalam pertanyaan Anda. Contohnya adalah pengontrol untuk printer ink jet yang saya rancang sekali. Dalam sistem tertanam, overhead memori sistem operasi mungkin tidak diperlukan.

Dalam situasi memori ketat, saya akan melihat karakteristik kompiler saya berhadapan dengan set instruksi prosesor, misalnya. Dalam aplikasi di mana string diproses banyak, mungkin diinginkan untuk menggunakan deskriptor seperti panjang string. Saya sedang memikirkan sebuah kasus di mana CPU sangat efisien dalam bekerja dengan offset pendek dan / atau offset relatif dengan register alamat.

Jadi mana yang lebih penting dalam aplikasi Anda: ukuran dan efisiensi kode, atau kompatibilitas dengan OS atau Perpustakaan? Pertimbangan lain mungkin pemeliharaan. Semakin jauh Anda menyimpang dari konvensi, semakin sulit bagi orang lain untuk mempertahankannya.

Hugh Buntu
sumber
1

Orang lain telah membahas masalah bahwa dalam C, string sebagian besar adalah apa yang Anda dapatkan dari mereka. Tetapi tampaknya ada beberapa kebingungan dalam pertanyaan Anda tentang terminator itu sendiri, dan dari satu perspektif, ini bisa menjadi hal yang dikhawatirkan oleh seseorang di posisi Anda.

String C diakhiri null. Artinya, mereka diakhiri oleh karakter nol NUL,. Mereka tidak diakhiri oleh null pointer NULL, yang merupakan jenis nilai yang sama sekali berbeda dengan tujuan yang sama sekali berbeda.

NULdijamin memiliki nilai integer nol. Di dalam string, itu juga akan memiliki ukuran tipe karakter yang mendasarinya, yang biasanya akan menjadi 1.

NULLtidak dijamin memiliki tipe integer sama sekali. NULLdimaksudkan untuk digunakan dalam konteks pointer, dan umumnya diharapkan memiliki tipe pointer, yang seharusnya tidak dikonversi ke karakter atau integer jika kompiler Anda bagus. Walaupun definisi NULLmelibatkan mesin terbang 0, itu tidak dijamin untuk benar-benar memiliki nilai [1], dan kecuali jika kompiler Anda mengimplementasikan konstanta sebagai satu karakter #define(banyak yang tidak, karena NULL benar - benar tidak boleh bermakna dalam non-karakter). pointer konteks), karena itu kode yang diperluas tidak dijamin untuk benar-benar melibatkan nilai nol (meskipun membingungkan memang melibatkan mesin terbang nol).

Jika NULLdiketik, kemungkinan juga tidak akan memiliki ukuran 1 (atau ukuran karakter lain). Ini mungkin dapat menyebabkan masalah tambahan, meskipun konstanta karakter aktual tidak memiliki ukuran karakter baik sebagian besar.

Sekarang kebanyakan orang akan melihat ini dan berpikir, "null pointer sebagai apa pun selain semua-nol-bit? Omong kosong" - tetapi asumsi seperti itu hanya aman pada platform umum seperti x86. Karena Anda secara eksplisit menyebutkan minat untuk menargetkan platform lain, Anda perlu mempertimbangkan masalah ini, karena Anda telah secara eksplisit memisahkan kode Anda dari asumsi tentang sifat hubungan antara pointer dan integer.

Oleh karena itu, sementara string C adalah null-dihentikan, mereka tidak diakhiri oleh NULL, tetapi oleh NUL(biasanya ditulis '\0'). Kode yang secara eksplisit digunakan NULLsebagai terminator string akan bekerja pada platform dengan struktur alamat langsung, dan bahkan akan dikompilasi dengan banyak kompiler, tetapi sama sekali tidak benar C.


[1] nilai null pointer yang sebenarnya dimasukkan oleh kompiler ketika membaca 0 token dalam konteks di mana ia akan dikonversi ke tipe pointer. Ini bukan konversi dari bilangan bulat nilai 0, dan tidak dijamin untuk terus jika apa pun selain token 0itu sendiri digunakan, seperti nilai dinamis dari variabel; konversi juga tidak dapat dibalik, dan penunjuk nol tidak harus menghasilkan nilai 0 saat dikonversi ke integer.

Leushenko
sumber
Poin yang bagus. Saya telah mengirimkan hasil edit untuk membantu menjernihkan ini.
Monty Harder
" NULDijamin memiliki nilai integer nol." -> C tidak mendefinisikan NUL. Sebaliknya C mendefinisikan bahwa string memiliki chracter null akhir , byte dengan semua bit diatur ke 0.
chux - Reinstate Monica
1

Saya telah menggunakan string dalam C, itu berarti karakter dengan terminasi nol disebut Strings.

Ini tidak akan memiliki masalah ketika Anda menggunakan di baremetal atau di sistem operasi apa pun seperti Windows, Linux, RTOS: (FreeRTO, OSE).

Dalam embedded null terminasi dunia sebenarnya membantu lebih banyak token karakter sebagai string.

Saya telah menggunakan string dalam C seperti itu di banyak sistem kritis keselamatan.

Anda mungkin bertanya-tanya, apa sebenarnya string dalam C?

String C-style, yang merupakan array, ada juga string literal, seperti "ini". Pada kenyataannya, kedua tipe string ini hanyalah kumpulan karakter yang duduk bersebelahan dalam memori.

Setiap kali Anda menulis string, diapit dengan tanda kutip ganda, C secara otomatis membuat array karakter untuk kami, berisi string itu, diakhiri oleh karakter \ 0.

Misalnya, Anda dapat mendeklarasikan dan mendefinisikan array karakter, dan menginisialisasi dengan konstanta string:

char string[] = "Hello cruel world!";

Jawaban langsung: Anda tidak benar-benar perlu khawatir tentang penggunaan karakter dengan penghentian nol, karya ini terlepas dari platform apa pun.

danglingpointer
sumber
Terima kasih, tidak tahu bahwa ketika dinyatakan dengan tanda kutip ganda, a NULsecara otomatis ditambahkan.
Mengintai
1

Seperti yang dikatakan orang lain, terminasi nol cukup universal untuk standar C. Tetapi (seperti yang juga ditunjukkan orang lain) tidak 100%. Sebagai contoh (lain), sistem operasi VMS biasanya menggunakan apa yang disebutnya "deskriptor string" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html diakses di C oleh #include <descrip.h >

Hal-hal tingkat aplikasi dapat menggunakan terminasi nol atau tidak, namun pengembang menganggapnya sesuai. Tetapi hal-hal VMS tingkat rendah benar-benar membutuhkan deskriptor, yang tidak menggunakan terminasi nol sama sekali (lihat tautan di atas untuk perincian). Ini sebagian besar agar semua bahasa (C, assembly, dll) yang secara langsung menggunakan VMS internal dapat memiliki antarmuka yang sama dengannya.

Jadi, jika Anda mengantisipasi segala jenis situasi serupa, Anda mungkin ingin lebih berhati-hati daripada yang mungkin disarankan "penghentian nol universal". Saya akan lebih berhati-hati jika saya melakukan apa yang Anda lakukan, tetapi untuk hal-hal tingkat aplikasi saya aman untuk menganggap pengakhiran nol. Saya tidak akan menyarankan tingkat keamanan yang sama untuk Anda. Kode Anda mungkin harus berinteraksi dengan assembly, dan / atau lainnya, kode bahasa di beberapa titik di masa depan, yang mungkin tidak selalu sesuai dengan standar C dari string yang diakhiri dengan null.

John Forkosh
sumber
Hari ini, 0 terminasi sebenarnya sangat tidak biasa. C ++ std :: string tidak, String Java tidak, Objective-C NSString tidak, Swift String tidak - sebagai hasilnya, setiap perpustakaan bahasa mendukung string dengan kode NUL di dalam string (yang tidak mungkin dengan C string untuk alasan yang jelas).
gnasher729
@ gnasher729 Saya mengubah "... cukup universal" menjadi "universal untuk standar C", yang saya harap dapat menghilangkan ambiguitas dan tetap benar hingga hari ini (dan itulah yang saya maksudkan, sesuai dengan subjek dan pertanyaan OP).
John Forkosh
0

Dalam pengalaman saya tentang embedded, safety kritis dan sistem waktu nyata, tidak jarang menggunakan konvensi string C dan PASCAL, yaitu untuk memasok panjang string sebagai karakter pertama, (yang membatasi panjang hingga 255), dan untuk mengakhiri string dengan setidaknya satu 0x00, ( NUL), yang mengurangi ukuran yang dapat digunakan menjadi 254.

Salah satu alasannya adalah untuk mengetahui berapa banyak data yang Anda harapkan setelah byte pertama diterima dan yang lain adalah bahwa, dalam sistem seperti itu, ukuran buffer dinamis dihindari jika memungkinkan - mengalokasikan 256 ukuran buffer tetap lebih cepat dan lebih aman, (tidak ada perlu memeriksa jika mallocgagal). Lain adalah bahwa sistem lain yang berkomunikasi dengan Anda mungkin tidak ditulis dalam ANSI-C.

Dalam setiap pekerjaan tertanam, penting untuk membuat dan memelihara Dokumen Kontrol Antarmuka (IDC), yang mendefinisikan semua struktur komunikasi Anda termasuk format string, endianness, ukuran integer, dll., Sesegera mungkin, ( idealnya sebelum memulai ), dan itu harus Anda, dan semua tim, kitab suci ketika menulis sistem - jika seseorang ingin memperkenalkan struktur atau format baru itu harus didokumentasikan di sana terlebih dahulu dan semua orang yang mungkin terkena informasi, mungkin dengan opsi untuk memveto perubahan .

Steve Barnes
sumber