Membaca string dengan scanf

147

Saya sedikit bingung tentang sesuatu. Saya mendapat kesan bahwa cara yang benar membaca string C dengan scanf()berjalan sepanjang garis

(apalagi buffer overflow yang mungkin, itu hanya contoh sederhana)

char string[256];
scanf( "%s" , string );

Namun, berikut ini tampaknya berfungsi juga,

scanf( "%s" , &string );

Apakah ini hanya kompiler (gcc) saya, keberuntungan murni, atau yang lainnya?

abeln
sumber
Dalam kasus kedua, sebenarnya tidak ada buffer overflow yang mungkin terjadi, karena Anda sama sekali tidak menggunakan buffer itu. Entah itu, atau Anda bisa mengatakan bahwa string apa pun yang lebih besar dari 3 karakter akan meluap "buffer" Anda.
TED
Saya merujuk pada contoh pertama. Juga, orang lain sudah menunjukkan apa yang terjadi di sini.
abeln
Ya. Mencobanya, dan Gareth benar. Aneh.
TED
Karena pertanyaan ini dikembalikan oleh pencarian untuk "cara membaca string dengan scanf", mungkin tepat untuk menunjukkan bahwa pertanyaan ini adalah tentang cara untuk melewatkan pointer ke buffer scanf, dan pertanyaan dan jawaban yang diterima berfokus pada itu, dan hilangkan pembatasan yang sangat penting untuk panjang input maksimum yang harus digunakan dalam kode nyata (tetapi selain itu poin untuk pertanyaan ini).
Arkku

Jawaban:

141

Array "meluruh" menjadi sebuah pointer ke elemen pertama, jadi scanf("%s", string)sama dengan scanf("%s", &string[0]). Di sisi lain, scanf("%s", &string)melewati pointer-to- char[256], tetapi menunjuk ke tempat yang sama.

Kemudian scanf, ketika memproses ekor dari daftar argumennya, akan mencoba menarik keluar a char *. Itulah Hal yang Tepat ketika Anda telah melewati stringatau &string[0], tetapi ketika Anda telah melewati &stringAnda tergantung pada sesuatu yang tidak dijamin standar bahasa, yaitu bahwa pointer &stringdan &string[0]- pointer ke objek dari berbagai jenis dan ukuran yang mulai dari tempat yang sama - direpresentasikan dengan cara yang sama.

Saya tidak percaya saya pernah menemukan sistem yang tidak bekerja, dan dalam praktiknya Anda mungkin aman. Namun, itu salah, dan itu bisa gagal pada beberapa platform. (Contoh hipotetis: implementasi "debugging" yang mencakup mengetikkan informasi dengan setiap pointer. Saya pikir implementasi C pada Simbol "Lisp Machines" melakukan sesuatu seperti ini.)

Gareth McCaughan
sumber
5
+1. Ini juga sepele untuk memverifikasi bahwa hasil peluruhan array dalam &stringbekerja sama dengan string(alih-alih mengakibatkan kerusakan memori acak, karena jawaban lain salah menyatakan):printf("%x\n%x\n", string, &string);
Josh Kelley
@Josh Kelley menarik, saya akan menerimanya bahwa ini tidak akan menjadi kasus dengan pointer ke string yang dialokasikan melalui malloc ()?
abeln
4
@abeln: Benar. Untuk string yang dialokasikan melalui malloc (), stringadalah sebuah pointer, dan &stringmerupakan alamat dari pointer itu, sehingga keduanya TIDAK dapat dipertukarkan. Seperti yang dijelaskan Gareth, bahkan untuk kasus peluruhan array, stringdan &stringsecara teknis bukan tipe yang sama (meskipun mereka dapat dipertukarkan untuk sebagian besar arsitektur), dan gcc akan memberikan peringatan untuk contoh kedua Anda jika Anda menghidupkan -Wall.
Josh Kelley
@Josh Kelley: terima kasih, saya senang saya bisa menjernihkan pikiran saya tentang ini.
abeln
1
@JCooper str, & str [0] keduanya mewakili hal yang sama (awal array), dan meskipun tidak selalu & str juga merupakan alamat memori yang sama. Sekarang strPtr menunjuk ke str sehingga alamat memori yang tersimpan di dalam strPtr sama dengan str dan 12fe60. Akhirnya & strPtr adalah alamat dari variabel strPtr, ini bukan nilai yang disimpan dalam strptr tetapi alamat memori aktual dari strPtr. Karena strptr adalah variabel yang berbeda dari yang lain, ia juga memiliki alamat memori yang berbeda, dalam hal ini 12fe54
Juan Besa
-9

Saya pikir ini di bawah ini akurat dan mungkin membantu. Jangan ragu untuk memperbaikinya jika Anda menemukan kesalahan. Saya baru di C.

char str[]  
  1. array nilai tipe char, dengan alamatnya sendiri di memori
  2. array nilai dari tipe char, dengan alamatnya sendiri dalam memori sebanyak alamat berturut-turut sebagai elemen dalam array
  3. termasuk terminasi karakter nol '\0' &str, &str[0]dan str, ketiganya mewakili lokasi yang sama dalam memori yang merupakan alamat elemen pertama arraystr

    char * strPtr = & str [0]; // deklarasi dan inisialisasi

sebagai alternatif, Anda dapat membaginya menjadi dua:

char *strPtr; strPtr = &str[0];
  1. strPtr adalah pointer ke a char
  2. strPtr menunjuk pada array str
  3. strPtr adalah variabel dengan alamatnya sendiri di memori
  4. strPtr adalah variabel yang menyimpan nilai alamat &str[0]
  5. strPtr alamat sendiri di memori berbeda dari alamat memori yang disimpannya (alamat array di memori alias & str [0])
  6. &strPtr mewakili alamat strPtr itu sendiri

Saya pikir Anda bisa mendeklarasikan pointer ke pointer sebagai:

char **vPtr = &strPtr;  

mendeklarasikan dan menginisialisasi dengan alamat penunjuk strPtr

Atau Anda dapat membelah menjadi dua:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr menunjuk pada strPtr pointer
  2. *vPtr adalah variabel dengan alamatnya sendiri di memori
  3. *vPtr adalah variabel yang menyimpan nilai alamat & strPtr
  4. komentar terakhir: Anda tidak dapat melakukannya str++, stralamat adalah const, tetapi Anda dapat melakukannyastrPtr++
angelita
sumber