Bagaimana seharusnya array karakter digunakan sebagai string?

10

Saya mengerti bahwa string dalam C hanyalah array karakter. Jadi saya mencoba kode berikut, tetapi memberikan hasil yang aneh, seperti keluaran sampah atau crash program:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

Mengapa ini tidak berhasil?

Ini dikompilasi dengan bersih gcc -std=c17 -pedantic-errors -Wall -Wextra.


Catatan: Posting ini dimaksudkan untuk digunakan sebagai FAQ kanonik untuk masalah yang berasal dari kegagalan untuk mengalokasikan ruang untuk terminator NUL saat mendeklarasikan string.

Lundin
sumber

Jawaban:

12

AC string adalah array karakter yang diakhiri dengan terminator nol .

Semua karakter memiliki nilai tabel simbol. Terminator nol adalah nilai simbol 0(nol). Ini digunakan untuk menandai akhir sebuah string. Ini diperlukan karena ukuran string tidak disimpan di mana pun.

Oleh karena itu, setiap kali Anda mengalokasikan ruang untuk string, Anda harus menyertakan ruang yang cukup untuk karakter terminator nol. Contoh Anda tidak melakukan ini, itu hanya mengalokasikan ruang untuk 5 karakter "hello". Kode yang benar adalah:

char str[6] = "hello";

Atau dengan kata lain, Anda dapat menulis kode self-documenting untuk 5 karakter plus 1 null terminator:

char str[5+1] = "hello";

Saat mengalokasikan memori untuk string secara dinamis saat dijalankan, Anda juga perlu mengalokasikan ruang untuk terminator nol:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

Jika Anda tidak menambahkan terminator nol di akhir string, maka fungsi perpustakaan yang mengharapkan string tidak akan berfungsi dengan baik dan Anda akan mendapatkan bug "perilaku tidak terdefinisi" seperti keluaran sampah atau crash program.

Cara yang paling umum untuk menulis karakter null terminator di C adalah dengan menggunakan apa yang disebut "oktal urutan escape", tampak seperti ini: '\0'. Ini 100% setara dengan menulis 0, tetapi \berfungsi sebagai kode yang mendokumentasikan diri untuk menyatakan bahwa nol secara eksplisit dimaksudkan sebagai terminator nol. Kode seperti itu if(str[i] == '\0')akan memeriksa apakah karakter spesifiknya adalah terminator nol.

Harap dicatat bahwa istilah terminator nol tidak ada hubungannya dengan pointer nol atau NULLmakro! Ini bisa membingungkan - nama yang sangat mirip tetapi artinya sangat berbeda. Inilah sebabnya mengapa terminator nol kadang-kadang disebut NULdengan satu L, tidak menjadi bingung dengan NULLatau null pointer. Lihat jawaban untuk pertanyaan SO ini untuk perincian lebih lanjut.

Dalam "hello"kode Anda disebut string literal . Ini harus dianggap sebagai string hanya baca. The ""sintaks berarti bahwa kompiler akan menambahkan sebuah nol terminator pada akhir string literal otomatis. Jadi jika Anda mencetak sizeof("hello")Anda akan mendapatkan 6, bukan 5, karena Anda mendapatkan ukuran array termasuk terminator nol.


Kompilasi ini bersih dengan gcc

Bahkan bukan peringatan. Ini karena detail halus / cacat dalam bahasa C yang memungkinkan array karakter diinisialisasi dengan string literal yang berisi persis karakter sebanyak ada ruang dalam array dan kemudian diam-diam membuang terminator nol (C17 6.7.9 / 15). Bahasa ini sengaja berperilaku seperti ini untuk alasan historis, lihat diagnostik gcc tidak konsisten untuk inisialisasi string untuk detail. Perhatikan juga bahwa C ++ berbeda di sini dan tidak memungkinkan trik / cacat ini digunakan.

Lundin
sumber
1
Anda harus menyebutkan char str[] = "hello";kopernya.
Jabberwocky
@Jabberwocky Ini adalah wiki komunitas, silakan mengedit dan berkontribusi.
Lundin
1
... dan mungkin juga char *str = "hello";... str[0] = foo;masalahnya.
Jabberwocky
Mungkin memperluas implikasi sizeofpenggunaannya pada parameter fungsi, terutama ketika didefinisikan sebagai sebuah array.
Weather Vane
@WeatherVane Harus dibahas oleh FAQ lain di sini: stackoverflow.com/questions/492384/…
Lundin
4

Dari Standar C (7.1.1 Definisi istilah)

1 String adalah urutan karakter yang berdekatan yang diakhiri oleh dan termasuk karakter nol pertama. Istilah string multibyte kadang-kadang digunakan sebagai gantinya untuk menekankan pemrosesan khusus yang diberikan kepada karakter multibyte yang terkandung dalam string atau untuk menghindari kebingungan dengan string lebar. Penunjuk ke string adalah penunjuk ke karakter awal (terendah yang dialamatkan). Panjang string adalah jumlah byte sebelum karakter nol dan nilai string adalah urutan nilai karakter yang terkandung, secara berurutan.

Dalam deklarasi ini

char str [5] = "hello";

string literal "hello"memiliki representasi internal seperti

{ 'h', 'e', 'l', 'l', 'o', '\0' }

sehingga memiliki 6 karakter termasuk nol penghentian. Elemen-elemennya digunakan untuk menginisialisasi array karakter stryang hanya menyediakan ruang untuk 5 karakter.

Standar C (berseberangan dengan Standar C ++) memungkinkan inisialisasi array karakter ketika nol penghentian string literal tidak digunakan sebagai penginisialisasi.

Namun akibatnya array karakter strtidak mengandung string.

Jika Anda ingin array akan berisi string Anda bisa menulis

char str [6] = "hello";

atau hanya

char str [] = "hello";

Dalam kasus terakhir ukuran array karakter ditentukan dari jumlah inisialisasi string literal yang sama dengan 6.

Vlad dari Moskow
sumber
0

Dapatkah semua string dianggap sebagai array karakter ( Ya ), dapatkah semua array karakter dianggap string ( Tidak ).

Kenapa tidak? dan mengapa itu penting?

Selain jawaban lain yang menjelaskan bahwa panjang string tidak disimpan di mana pun sebagai bagian dari string dan referensi ke standar di mana string didefinisikan, sisi lain adalah "Bagaimana fungsi C library menangani string?"

Sementara array karakter dapat menampung karakter yang sama, itu hanya array karakter kecuali karakter terakhir diikuti oleh karakter nul-terminating . Itu nul-mengakhiri karakter adalah apa yang memungkinkan array karakter dipertimbangkan (ditangani sebagai) string.

Semua fungsi dalam C yang mengharapkan string sebagai argumen mengharapkan urutan karakter akan diakhiri . Mengapa?

Ini ada hubungannya dengan cara semua fungsi string bekerja. Karena panjang tidak dimasukkan sebagai bagian dari array, fungsi string, pindai maju dalam array sampai karakter-nul (misalnya '\0'- setara dengan desimal 0) ditemukan. Lihat Tabel dan Deskripsi ASCII . Terlepas apakah Anda menggunakan strcpy, strchr, strcspn, dll .. Semua fungsi string bergantung pada nul-terminating karakter yang hadir untuk menentukan di mana akhir string yang.

Perbandingan dua fungsi serupa dari string.hakan menekankan pentingnya karakter nul-terminating . Ambil contoh:

    char *strcpy(char *dest, const char *src);

The strcpyFungsi hanya salinan byte dari srcke destsampai nul-terminating karakter ditemukan jitu strcpydi mana harus berhenti menyalin karakter. Sekarang ambil fungsi yang sama memcpy:

    void *memcpy(void *dest, const void *src, size_t n);

Fungsi melakukan operasi serupa, tetapi tidak mempertimbangkan atau memerlukan srcparameter untuk menjadi string. Karena memcpytidak dapat hanya memindai ke depan dalam srcmenyalin byte desthingga karakter nul-terminating tercapai, itu memerlukan jumlah byte eksplisit untuk menyalin sebagai parameter ketiga. Parameter ketiga ini memberikan memcpyinformasi ukuran yang sama strcpyyang dapat diturunkan hanya dengan memindai ke depan sampai karakter nul-terminating ditemukan.

(yang juga menekankan apa yang salah strcpy(atau fungsi apa pun yang mengharapkan string) jika Anda gagal menyediakan fungsi dengan string nul-dihentikan - ia tidak tahu di mana harus berhenti dan dengan senang hati akan berlomba melintasi seluruh segmen memori Anda menerapkan Perilaku Undefined sampai nul-karakter kebetulan ditemukan di suatu tempat di memori - atau Segmentation Fault terjadi)

Itulah sebabnya fungsi yang mengharapkan string nul-dihentikan harus melewati string nul-dihentikan dan mengapa itu penting .

David C. Rankin
sumber
0

Secara intuitif ...

Pikirkan sebuah array sebagai variabel (menyimpan sesuatu) dan string sebagai nilai (dapat ditempatkan dalam variabel).

Mereka tentu bukan hal yang sama. Dalam kasus Anda, variabelnya terlalu kecil untuk menahan string, sehingga string terputus. ("string yang dikutip" dalam C memiliki karakter nol implisit di akhir.)

Namun dimungkinkan untuk menyimpan string dalam array yang jauh lebih besar dari string.

Perhatikan bahwa operator penugasan dan pembanding biasa ( = == <dll.) Tidak berfungsi seperti yang Anda harapkan. Tetapi strxyzkeluarga fungsi cukup dekat, begitu Anda tahu apa yang Anda lakukan. Lihat FAQ C tentang string dan array .

Artelius
sumber