İşte yardımcı olacağını umduğum ayrıntılı bir açıklama. Açıklaması en basit olan programınız ile başlayalım.
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
İlk ifade:
const char* p = "Hello";
p
bir işaretçi olarak ilan eder char
. "Bir işaretçisi" dediğimizde char
, bu ne anlama geliyor? Değerinin p
a'nın adresi olduğu anlamına gelir char
; p
bize hafızanın neresinde a tutmak için ayrılmış bir boşluk olduğunu söyler char
.
İfade ayrıca p
dize değişmezindeki ilk karakteri gösterecek şekilde başlatılır "Hello"
. Bu alıştırma uğruna p
, tüm dizeyi değil, yalnızca ilk karakteri işaret ettiğini anlamak önemlidir 'H'
. Sonuçta , tüm dizeye değil, p
birine bir göstericidir char
. Değeri p
adresidir 'H'
in "Hello"
.
Ardından bir döngü oluşturursunuz:
while (*p++)
Döngü koşulu ne anlama *p++
geliyor? Burada bunu şaşırtıcı kılan üç şey var (en azından aşinalık oluşana kadar):
- İki operatörün önceliği, postfix
++
ve indirection*
- Bir sonek artış ifadesinin değeri
- Bir sonek artış ifadesinin yan etkisi
1. Öncelik . Operatörler için öncelik tablosuna hızlı bir bakış, sonek artışının dereference / indirection'dan (15) daha yüksek bir önceliğe (16) sahip olduğunu söyleyecektir. Karmaşık ifadesi olduğunu Bu araçlar *p++
gidiyor olarak gruplandırılabilir için: *(p++)
. Yani *
parça , parçanın değerine uygulanacaktır p++
. Öyleyse önce p++
bölümü ele alalım .
2. Sonek ifade değeri . Değeri p++
değeridir p
arttırmadan önce . Eğer varsa:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
çıktı:
7
8
çünkü i++
artımdan i
önce değerlendirir . Benzer şekilde p++
şu anki değeri de değerlendirilecek p
. Bildiğimiz gibi şu anki değeri p
adresidir 'H'
.
Yani şimdi p++
parçası *p++
değerlendirildi; şu anki değeridir p
. Sonra *
bölüm gerçekleşir. *(current value of p)
anlamı: tutulan adresteki değere erişmektir p
. Bu adresteki değerin olduğunu biliyoruz 'H'
. Yani ifade olarak *p++
değerlendirilir 'H'
.
Şimdi bir dakika dur diyorsun. Olarak *p++
değerlendirilirse 'H'
, bu neden 'H'
yukarıdaki kodda yazdırılmıyor? Yan etkiler burada devreye girer.
3. Postfix ifadesi yan etkileri . Sonek ++
, geçerli işlenenin değerine sahiptir , ancak bu işleneni artırmanın yan etkisine sahiptir. Ha? Bu int
koda tekrar bir göz atın :
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Daha önce belirtildiği gibi çıktı şu şekilde olacaktır:
7
8
Ne zaman i++
birinci değerlendirilir printf()
, bu 7'ye değerlendirir Ama bir noktada, ikinci daha önce C standart garanti printf()
yürütmeye başlar, yan etki arasında ++
operatör yerini almış olacaktır. Yani, ikinci printf()
gerçekleşmeden önce , ilkinde operatörün i
bir sonucu olarak artmış olacaktır . Bu arada, bu, standardın yan etkilerin zamanlamasıyla ilgili verdiği birkaç garantiden biridir.++
printf()
Kodunuzda, daha sonra ifade *p++
değerlendirildiğinde, olarak değerlendirilir 'H'
. Ama buna vardığınızda:
printf ("%c", *p)
bu sinir bozucu yan etki meydana geldi. p
artırıldı. Oha! Artık işaret etmiyor 'H'
, geçmiş bir karaktere işaret ediyor 'H'
: 'e'
diğer bir deyişle. Bu, doğal çıktınızı açıklar:
ello
Bu nedenle, diğer cevaplardaki yardımcı (ve doğru) öneriler korosu: Alınan Telaffuz'u basmak için "Hello"
, onun kokain karşılığı değil, gibi bir şeye ihtiyacınız var.
while (*p)
printf ("%c", *p++);
Bunun için çok fazla. Gerisi ne olacak? Bunların anlamlarını soruyorsunuz:
*ptr++
*++ptr
++*ptr
Biz sadece bu yüzden saniyede Bakalım, ilk hakkında konuştuk: *++ptr
.
Önceki açıklamamızda son ek artışının p++
belirli bir önceliğe , bir değere ve bir yan etkiye sahip olduğunu gördük . Önek artışı ++p
, sonek karşılığıyla aynı yan etkiye sahiptir : işleneni 1 artırır. Ancak, farklı bir önceliği ve farklı bir değeri vardır .
Önek artışının önceliği, sonekten daha düşüktür; 15 önceliğine sahiptir. Başka bir deyişle, referans / dolaylı yönlendirme operatörü ile aynı önceliğe sahiptir *
. Gibi bir ifadede
*++ptr
önemli olan öncelik değildir: iki operatör öncelik bakımından aynıdır. Böylece ilişkilendirilebilirlik devreye giriyor. Önek artışı ve dolaylama operatörü sağ-sol ilişkilendirilebilirliğe sahip. Bu ilişkilendirilebilirlik nedeniyle, işlenen ptr
, operatörden ++
önce en sağdaki operatörle daha çok solda gruplanacaktır *
. Başka bir deyişle, ifade gruplanacak *(++ptr)
. Öyleyse, olduğu gibi, *ptr++
ancak farklı bir nedenle, burada da *
parça parçanın değerine uygulanacaktır ++ptr
.
Peki bu değer nedir? Önek artış ifadesinin değeri, artıştan sonra işlenenin değeridir . Bu, onu sonek artırma operatöründen çok farklı bir canavar yapar. Diyelim ki sahipsin:
int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
Çıktı şu şekilde olacaktır:
8
8
... postfix operatörüyle gördüğümüzden farklı. Benzer şekilde, eğer varsa:
const char* p = "Hello";
printf ("%c ", *p); // note space in format string
printf ("%c ", *++p); // value of ++p is p after the increment
printf ("%c ", *p++); // value of p++ is p before the increment
printf ("%c ", *p); // value of p has been incremented as a side effect of p++
çıktı:
H e e l // good dog
Nedenini anlıyor musun?
Şimdi sorduğunuz üçüncü ifadeye geliyoruz ++*ptr
. Aslında en zor olanı bu. Her iki operatör de aynı önceliğe ve sağ-sol ilişkilendirilebilirliğe sahiptir. Bu, ifadenin gruplanacağı anlamına gelir ++(*ptr)
. ++
Parçası değerine uygulanacak *ptr
parçası.
Yani eğer sahipsek:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
şaşırtıcı derecede egoist çıktı şöyle olacak:
I
Ne?! Tamam, yani *p
bölüm olarak değerlendirilecek 'H'
. Sonra ++
oyuna giriyor, bu noktada, 'H'
göstericiye değil, işaretleyiciye uygulanacak ! 1'i eklediğinizde ne olur 'H'
? 1 artı ASCII değeri 'H'
, 72 elde edersiniz ; Eğer 73. Bir şekilde bu Temsil olsun char
ve sen olsun char
73 ASCII değeri: 'I'
.
Bu, sorunuzda sorduğunuz üç ifadeyle ilgilenir. İşte sorunuza yapılan ilk yorumda bahsedilen bir başkası:
(*ptr)++
Bu da ilginç. Eğer varsa:
char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
size şu coşkulu çıktıyı verecektir:
HI
Neler oluyor? Yine, bu bir öncelik , ifade değeri ve yan etkiler meselesidir . Parantezler nedeniyle, *p
parça birincil ifade olarak kabul edilir. Birincil ifadeler diğer her şeyi gölgede bırakır; önce değerlendirilirler. Ve *p
bildiğiniz gibi değerlendiriyor 'H'
. İfadenin geri kalanı olan ++
kısım bu değere uygulanır. Yani, bu durumda (*p)++
olur 'H'++
.
Değeri nedir 'H'++
? Eğer 'I'
dediyseniz, sonek artışıyla değer ve yan etki tartışmamızı (zaten!) Unuttunuz. Unutmayın, şu anki değeri ile'H'++
değerlendirilir . Böylece ilk önce basılacak . Sonra, bir yan etki olarak , bu artacak . İkincisi bunu yazdırır . Ve neşeli selamınız var. 'H'
printf()
'H'
'H'
'I'
printf()
'I'
Pekala, ama bu son iki durumda neden ihtiyacım var
char q[] = "Hello";
char* p = q;
Neden böyle bir şeye sahip olamıyorum
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
Çünkü "Hello"
bir dize değişmezidir. Eğer denerseniz ++*p
, 'H'
dizeyi olarak değiştirmeye çalışıyorsunuz 'I'
ve tüm dizeyi oluşturuyorsunuz "Iello"
. C'de dize değişmezleri salt okunurdur; bunları değiştirmeye çalışmak, tanımlanmamış davranışa neden olur. "Iello"
İngilizcede de tanımsız, ama bu sadece tesadüf.
Tersine, sahip olamazsın
char p[] = "Hello";
printf ("%c", *++p); // attempting to modify value of array identifier!
Neden olmasın? Çünkü bu örnekte p
bir dizidir. Bir dizi değiştirilebilir bir l-değeri değildir; p
Önceden veya artarak artırma veya azaltma ile noktaları değiştiremezsiniz , çünkü dizinin adı sabit bir işaretçi gibi çalışır. (Aslında öyle değil; bu, ona bakmanın uygun bir yolu.)
Özetlemek gerekirse, sorduğunuz üç şey şunlardır:
*ptr++ // effectively dereferences the pointer, then increments the pointer
*++ptr // effectively increments the pointer, then dereferences the pointer
++*ptr // effectively dereferences the pointer, then increments dereferenced value
Ve işte dördüncü, her biri diğer üçü kadar eğlenceli:
(*ptr)++ // effectively forces a dereference, then increments dereferenced value
ptr
Aslında bir dizi tanımlayıcısıysa birinci ve ikinci çökecektir . Üçüncü ve dördüncü, ptr
bir dizgeye işaret ediyorsa çökecektir.
İşte aldın. Umarım artık hepsi kristaldir. Harika bir seyirci oldunuz ve bütün hafta burada olacağım.
(*ptr)++
(belirsizliği*ptr++