Apa perbedaan antara array char dan pointer char di C?


216

Saya mencoba memahami pointer dalam C tetapi saya saat ini bingung dengan yang berikut:

  • char *p = "hello"

    Ini adalah pointer char yang menunjuk pada array karakter, mulai dari h .

  • char p[] = "hello"

    Ini adalah array yang menyimpan halo .

Apa perbedaannya ketika saya meneruskan kedua variabel ini ke fungsi ini?

void printSomething(char *p)
{
    printf("p: %s",p);
}

5
Ini tidak valid: char p[3] = "hello";String initializer terlalu panjang untuk ukuran array yang Anda nyatakan. Salah ketik?
Cody Gray

16
Atau char p[]="hello";sudah cukup!
deepdive


1
kemungkinan rangkap dua Apa perbedaan antara char s [] dan char * s di C? Benar, ini juga menanyakan secara khusus tentang parameter fungsi, tetapi itu tidak charspesifik.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Anda perlu memahami bahwa mereka pada dasarnya berbeda. satu-satunya kesamaan dalam hal ini adalah bahwa pangkalan arry p [] adalah pointer const yang memungkinkan untuk mengakses array p [] melalui sebuah pointer. p [] sendiri menyimpan memori untuk sebuah string, sedangkan * p hanya menunjuk ke alamat elemen pertama hanya SATU CHAR (mis., menunjuk ke basis string yang sudah dialokasikan). Untuk menggambarkan hal ini dengan lebih baik, pertimbangkan di bawah ini: char * cPtr = {'h', 'e', ​​'l', 'l', 'o', '\ 0'}; ==> ini adalah kesalahan, karena cPtr adalah penunjuk hanya karakter char cBuff [] = {'h', 'e', ​​'l', 'l', 'o', 'o', '\ 0'}; ==> Ini Ok, bcos cBuff sendiri adalah array char
Ilavarasan

Jawaban:


222

char*dan char[] berbagai jenis , tetapi tidak segera terlihat dalam semua kasus. Ini karena array peluruhan menjadi pointer , yang berarti bahwa jika ekspresi tipe char[]disediakan di mana salah satu tipe char*diharapkan, kompiler secara otomatis mengubah array menjadi pointer ke elemen pertama.

Fungsi contoh Anda printSomethingmengharapkan penunjuk, jadi jika Anda mencoba meneruskan array ke sana seperti ini:

char s[10] = "hello";
printSomething(s);

Kompiler berpura-pura bahwa Anda menulis ini:

char s[10] = "hello";
printSomething(&s[0]);

Apakah ada yang berubah dari 2012 ke sekarang. Untuk array karakter "s" mencetak seluruh array .. yaitu, "halo"
Bhanu Tez

@ BhanuTez Tidak, bagaimana data disimpan dan apa yang dilakukan dengan data merupakan masalah tersendiri. Contoh ini mencetak seluruh string karena itulah cara printfmenangani %sformat string: mulai dari alamat yang disediakan dan lanjutkan sampai menemukan terminator nol. Jika Anda ingin mencetak hanya satu karakter, Anda dapat menggunakan %cstring format, misalnya.
iX3

Hanya ingin bertanya apakah char *p = "abc";karakter NULL \0ditambahkan secara otomatis seperti pada array char []?
KPMG

mengapa saya dapat mengatur char *name; name="123";tetapi dapat melakukan hal yang sama dengan inttipe? Dan setelah menggunakan %cuntuk mencetak name, output tidak dapat dibaca string yang: ?
TomSawyer

83

Ayo lihat:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo * dan foo [] adalah tipe yang berbeda dan mereka ditangani secara berbeda oleh kompiler (pointer = alamat + representasi dari tipe pointer, array = pointer + panjang opsional array, jika diketahui, misalnya, jika array dialokasikan secara statis ), perinciannya dapat ditemukan dalam standar. Dan pada tingkat runtime tidak ada perbedaan di antara mereka (di assembler, well, hampir, lihat di bawah).

Juga, ada pertanyaan terkait di FAQ C :

T : Apa perbedaan antara inisialisasi ini?

char a[] = "string literal";   
char *p  = "string literal";   

Program saya mogok jika saya mencoba menetapkan nilai baru ke p [i].

A : String literal (istilah formal untuk string kutipan ganda dalam sumber C) dapat digunakan dalam dua cara yang sedikit berbeda:

  1. Sebagai penginisialisasi untuk array char, seperti pada deklarasi char a [], ia menentukan nilai awal karakter dalam array tersebut (dan, jika perlu, ukurannya).
  2. Di tempat lain, itu berubah menjadi array karakter statis yang tidak disebutkan namanya, dan array yang tidak disebutkan namanya ini dapat disimpan dalam memori read-only, dan yang karenanya tidak dapat serta merta dimodifikasi. Dalam konteks ekspresi, array dikonversi sekaligus menjadi pointer, seperti biasa (lihat bagian 6), jadi deklarasi kedua menginisialisasi p untuk menunjuk ke elemen pertama array yang tidak disebutkan namanya itu.

Beberapa kompiler memiliki saklar yang mengontrol apakah literal string dapat ditulis atau tidak (untuk mengkompilasi kode lama), dan beberapa mungkin memiliki opsi untuk menyebabkan string literal diperlakukan secara formal sebagai array dari const char (untuk menangkap kesalahan yang lebih baik).

Lihat juga pertanyaan 1.31, 6.1, 6.2, 6.8, dan 11.8b.

Referensi: K & R2 Sec. 5.5 hal. 104

ISO Sec. 6.1.4, Sec. 6.5.7

Dasar Pemikiran Sec. 3.1.4

H&S Sec. 2.7.4 hal. 31-2


Dalam sizeof (q), mengapa q tidak membusuk menjadi pointer, seperti @Jon menyebutkan dalam jawabannya?
garyp

@garyp q tidak membusuk menjadi pointer karena sizeof adalah operator, bukan fungsi (bahkan jika sizeof adalah fungsi, q akan meluruh hanya jika fungsi tersebut mengharapkan pointer char).
GiriB

terima kasih, tetapi printf ("% u \ n" bukan printf ("% zu \ n", saya pikir Anda harus menghapus z.
Zakaria

33

Apa perbedaan antara char array vs char pointer di C?

C99 N1256 konsep

Ada dua penggunaan literal string karakter yang berbeda:

  1. Inisialisasi char[]:

    char c[] = "abc";      

    Ini "lebih banyak sihir", dan dijelaskan pada 6.7.8 / 14 "Inisialisasi":

    Array tipe karakter dapat diinisialisasi dengan string karakter literal, secara opsional tertutup dalam kurung. Karakter-karakter berturut-turut dari string karakter literal (termasuk karakter null terminating jika ada ruang atau jika array berukuran tidak diketahui) menginisialisasi elemen-elemen array.

    Jadi ini hanyalah jalan pintas untuk:

    char c[] = {'a', 'b', 'c', '\0'};

    Seperti array reguler lainnya, cdapat dimodifikasi.

  2. Di tempat lain: ia menghasilkan:

    Jadi ketika Anda menulis:

    char *c = "abc";

    Ini mirip dengan:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;

    Perhatikan pemeran implisit dari char[]ke char *, yang selalu sah.

    Kemudian jika Anda memodifikasi c[0], Anda juga memodifikasi __unnamed, yaitu UB.

    Ini didokumentasikan pada 6.4.5 "String literal":

    5 Dalam fase terjemahan 7, byte atau kode bernilai nol ditambahkan ke setiap urutan karakter multibyte yang dihasilkan dari string literal atau literal. Urutan karakter multibyte kemudian digunakan untuk menginisialisasi array durasi penyimpanan statis dan panjang hanya cukup untuk mengandung urutan. Untuk literal karakter string, elemen array memiliki tipe char, dan diinisialisasi dengan byte individu dari urutan karakter multibyte [...]

    6 Tidak ditentukan apakah array ini berbeda asalkan elemen mereka memiliki nilai yang sesuai. Jika program mencoba untuk memodifikasi array seperti itu, perilaku tidak terdefinisi.

6.7.8 / 32 "Inisialisasi" memberikan contoh langsung:

CONTOH 8: Deklarasi

char s[] = "abc", t[3] = "abc";

mendefinisikan objek char array "polos" sdan tyang elemennya diinisialisasi dengan literal karakter string.

Deklarasi ini identik dengan

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

Isi dari array dapat dimodifikasi. Di sisi lain, deklarasi

char *p = "abc";

mendefinisikan pdengan tipe "pointer to char" dan menginisialisasi untuk menunjuk ke objek dengan tipe "array of char" dengan panjang 4 yang elemennya diinisialisasi dengan karakter string literal. Jika ada upaya yang dilakukan untuk pmengubah isi array, perilaku tersebut tidak ditentukan.

Implementasi ELF GCC 4,8 x86-64

Program:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Kompilasi dan dekompilasi:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Output berisi:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Kesimpulan: GCC menyimpannya char*di .rodatabagian, bukan di .text.

Jika kami melakukan hal yang sama untuk char[]:

 char s[] = "abc";

kami memperoleh:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

sehingga disimpan di stack (relatif terhadap %rbp).

Namun perlu dicatat bahwa skrip tautan default menempatkan .rodatadan .textdi segmen yang sama, yang telah menjalankan tetapi tidak memiliki izin menulis. Ini dapat diamati dengan:

readelf -l a.out

yang mengandung:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

2
@ leszek.hanusz Perilaku Tidak Terdefinisi stackoverflow.com/questions/2766731/... Google "Bahasa C UB" ;-)
Ciro Santilli 郝海东 冠状 病 六四 六四 事件 法轮功

9

Anda tidak boleh mengubah konten konstanta string, yang merupakan tujuan pertama p. Yang kedua padalah array yang diinisialisasi dengan konstanta string, dan Anda dapat mengubah isinya.


6

Untuk kasus-kasus seperti ini, efeknya sama: Anda akhirnya melewati alamat karakter pertama dalam serangkaian karakter.

Deklarasi itu jelas tidak sama.

Berikut ini menyisihkan memori untuk string dan juga pointer karakter, dan kemudian menginisialisasi pointer untuk menunjuk ke karakter pertama dalam string.

char *p = "hello";

Sementara yang berikut menyisihkan memori hanya untuk string. Jadi sebenarnya bisa menggunakan memori lebih sedikit.

char p[10] = "hello";

codeplusplus.blogspot.com/2007/09/... "Namun, menginisialisasi variabel membutuhkan kinerja besar dan penalti ruang untuk array"
leef

@ Leef: Saya pikir itu tergantung di mana variabel berada. Jika itu dalam memori statis, saya pikir mungkin untuk array dan data disimpan dalam gambar EXE dan tidak memerlukan inisialisasi sama sekali. Kalau tidak, ya, tentu bisa lebih lambat jika data harus dialokasikan dan kemudian data statis harus disalin.
Jonathan Wood

3

Sejauh yang saya ingat, sebuah array sebenarnya adalah sekelompok pointer. Sebagai contoh

p[1]== *(&p+1)

adalah pernyataan yang benar


2
Saya akan menggambarkan array sebagai penunjuk ke alamat blok memori. Karenanya mengapa *(arr + 1)membawa Anda ke anggota kedua arr. Jika *(arr)menunjuk ke alamat memori 32-bit, mis. bfbcdf5e, Kemudian *(arr + 1)menunjuk ke bfbcdf60(byte kedua). Karenanya mengapa keluar dari lingkup array akan menyebabkan hasil yang aneh jika OS tidak segfault. Jika int a = 24;ada di alamat bfbcdf62, maka mengakses arr[2]mungkin kembali 24, dengan asumsi segfault tidak terjadi terlebih dahulu.
Braden Best

3

Dari APUE , Bagian 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

... Untuk templat pertama, nama dialokasikan pada tumpukan, karena kami menggunakan variabel array. Namun untuk nama kedua, kami menggunakan pointer. Dalam hal ini, hanya memori untuk penunjuk itu sendiri yang berada di tumpukan; kompiler mengatur agar string disimpan di segmen read-only dari executable. Ketika mkstempfungsi mencoba untuk memodifikasi string, kesalahan segmentasi terjadi.

Teks yang dikutip cocok dengan penjelasan @Ciro Santilli.


1

char p[3] = "hello"? harus char p[6] = "hello"diingat ada char '\ 0' di akhir "string" di C.

toh, array dalam C hanyalah sebuah penunjuk ke objek pertama dari objek penyesuaian dalam memori. satu-satunya yang berbeda adalah dalam semantik. sementara Anda dapat mengubah nilai pointer ke titik ke lokasi yang berbeda di memori, array, setelah dibuat, akan selalu menunjuk ke lokasi yang sama.
juga ketika menggunakan array "baru" dan "hapus" secara otomatis dilakukan untuk Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.