İş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";
pbir işaretçi olarak ilan eder char. "Bir işaretçisi" dediğimizde char, bu ne anlama geliyor? Değerinin pa'nın adresi olduğu anlamına gelir char; pbize hafızanın neresinde a tutmak için ayrılmış bir boşluk olduğunu söyler char.
İfade ayrıca pdize 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, pbirine bir göstericidir char. Değeri padresidir '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 padresidir '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 intkoda 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 ibir 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. partı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 *ptrparç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 *pbö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 charve sen olsun char73 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, *pparça birincil ifade olarak kabul edilir. Birincil ifadeler diğer her şeyi gölgede bırakır; önce değerlendirilirler. Ve *pbildiğ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 pbir 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
ptrAslında bir dizi tanımlayıcısıysa birinci ve ikinci çökecektir . Üçüncü ve dördüncü, ptrbir 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++