Scanf ile bir dizeyi okumak


148

Bir şey hakkında biraz kafam karıştı. Bir C dizgisini okumanın doğru yolunun scanf()şu satırlar boyunca gittiği izlenimindeydim

(olası arabellek taşmasına aldırma, bu sadece basit bir örnek)

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

Ancak, aşağıdakiler de işe yarıyor gibi görünüyor,

scanf( "%s" , &string );

Bu benim derleyicim mi (gcc), saf şans mı yoksa başka bir şey mi?


İkinci durumda, bu arabelleği hiç kullanmadığınız için aslında olası bir arabellek taşması yoktur. Ya öyle ya da 3 karakterden büyük herhangi bir dizenin "arabelleğinizden" taşacağını söyleyebilirsiniz.
TED

İlk örneğe atıfta bulunuyordum. Ayrıca, diğerleri burada neler olup bittiğine zaten işaret ettiler.
abeln

Evet. Denedim ve Gareth haklı. Tuhaf.
TED

Bu soru "scanf ile bir dizenin nasıl okunacağı" aramaları tarafından döndürüldüğünden, bu sorunun işaretçiyi tampona geçirme yolları scanfve hem soru hem de kabul edilen yanıt üzerinde odaklandığını belirtmek uygun olabilir . gerçek kodda kullanılması gereken (ancak bu sorunun yanı sıra) maksimum giriş uzunluğu için kritik öneme sahip kısıtlamaları atlayın .
Arkku

Yanıtlar:


141

Bir dizi, ilk elemanına bir göstericiye "bozunur", yani scanf("%s", string)eşdeğerdir scanf("%s", &string[0]). Öte yandan, scanf("%s", &string)bir işaretçiye geçer char[256], ancak aynı yere işaret eder.

Sonra scanf, argüman listesinin kuyruğunu işlerken, bir char *. Bu, geçtiğinizde stringveya geçtiğinizde Doğru Şeydir &string[0], ancak geçtiğinizde &string, dil standardının garanti etmediği bir şeye bağlı olursunuz, yani işaretçiler &stringve &string[0]- farklı tür ve boyutlardaki nesnelere işaret eden aynı yerden başlayın - aynı şekilde temsil edilir.

Bunun çalışmadığı bir sistemle hiç karşılaştığıma inanmıyorum ve pratikte muhtemelen güvendesiniz. Hiç de az değil, yanlış ve bazı platformlarda başarısız olabilir. (Varsayımsal örnek: her işaretçi ile tür bilgilerini içeren bir "hata ayıklama" uygulaması. Sanırım "Lisp Makineleri" Symbolics üzerindeki C uygulaması böyle bir şey yaptı.)


5
+1. Ayrıca, dizi bozulmasının &stringaynı şekilde çalıştığını doğrulamak da önemsizdir string(diğer yanıtların yanlış ileri sürdüğü gibi rastgele bellek bozulmasıyla sonuçlanmak yerine):printf("%x\n%x\n", string, &string);
Josh Kelley

@Josh Kelley ilginç, malloc () aracılığıyla tahsis edilen bir dizgeye bir işaretçi ile durumun böyle olmayacağını kabul ediyorum?
abeln

4
@abeln: Doğru. Malloc () aracılığıyla tahsis edilen bir dizge için string, bir göstericidir ve &stringbu göstericinin adresidir, bu nedenle ikisi birbirinin yerine DEĞİLDİR. Gareth'in açıkladığı gibi, dizi bozunması durumunda bile stringve &stringteknik olarak aynı türler değiller (çoğu mimari için birbirinin yerine geçebilir olsalar bile) ve gcc, açarsanız ikinci örneğiniz için bir uyarı verecektir -Wall.
Josh Kelley

@Josh Kelley: Teşekkürler, bu konudaki fikrimi temizleyebildiğim için mutluyum.
abeln

1
@JCooper str, & str [0] her ikisi de aynı şeyi (dizinin başlangıcını) temsil eder ve zorunlu olmamakla birlikte & str aynı bellek adresidir. Şimdi strPtr str'yi gösterir, böylece strPtr içinde depolanan bellek adresi str ile aynıdır ve bu 12fe60'dır. Son olarak & strPtr, strPtr değişkeninin adresidir, bu strptr'de depolanan değer değil, strPtr'nin gerçek bellek adresidir. Strptr, diğerlerinden farklı bir değişken olduğu için farklı bir hafıza adresine de sahiptir, bu durumda 12fe54
Juan Besa

-9

Bunun doğru olduğunu ve yardımcı olabileceğini düşünüyorum. Herhangi bir hata bulursanız düzeltmekten çekinmeyin. C'de yeniyim.

char str[]  
  1. bellekte kendi adresi olan char türünde değerler dizisi
  2. dizideki öğeler kadar ardışık adresler kadar bellekte kendi adresi olan char türünde değerler dizisi
  3. sonlandırma boş karakter içeren '\0' &str, &str[0]ve str, her üç dizinin ilk elemanın adresidir bellekte aynı konumu temsilstr

    karakter * strPtr = & str [0]; // bildirim ve başlatma

alternatif olarak, bunu ikiye bölebilirsiniz:

char *strPtr; strPtr = &str[0];
  1. strPtr bir göstericidir char
  2. strPtr dizideki noktalar str
  3. strPtr hafızasında kendi adresi olan bir değişkendir
  4. strPtr adresin değerini depolayan bir değişkendir &str[0]
  5. strPtr bellekteki kendi adresi, sakladığı bellek adresinden farklıdır (bellekteki dizinin adresi aka & str [0])
  6. &strPtr strPtr'nin adresini temsil eder

Bir işaretçiyi şu şekilde ilan edebileceğinizi düşünüyorum:

char **vPtr = &strPtr;  

strPtr işaretçisinin adresiyle bildirir ve başlatır

Alternatif olarak ikiye ayrılabilirsin:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr strPtr işaretçisindeki noktalar
  2. *vPtr hafızasında kendi adresi olan bir değişkendir
  3. *vPtr adres & strPtr değerini depolayan bir değişkendir
  4. son yorum: yapamazsın str++, stradres bir const, ama yapabilirsinstrPtr++
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.