Bellek adresi değilse C işareti tam olarak nedir?


206

C ile ilgili saygın bir kaynakta, &operatör tartışıldıktan sonra aşağıdaki bilgiler verilir :

... Terminolojinin [adresinin] kalması biraz talihsizdir , çünkü adreslerin ne hakkında olduğunu bilmeyenleri karıştırır ve yapanları yanıltır: işaretçileri adresler gibi düşünmek genellikle kedere yol açar. .

Okuduğum diğer materyaller (eşit derecede saygın kaynaklardan söyleyebilirim) her zaman utanmadan işaretçilere ve &operatöre bellek adresleri vermek olarak bahsetmiştir . Konunun gerçekliğini aramaya devam etmek isterim, ancak saygın kaynaklar KIND OF katılmıyorum zaman biraz zor.

Şimdi biraz kafam karıştı - bir bellek adresi değilse, tam olarak bir işaretçi nedir?

PS

Yazar daha sonra şöyle der: ... 'adres' terimini kullanmaya devam edeceğim, çünkü farklı bir tane [terim] icat etmek için daha da kötü olurdu.


118
Bir gösterici, bir olan değişken tutan bir adres. Ayrıca kendi adresi var. Bu, bir işaretçi ile dizi arasındaki temel farktır. Bir dizi etkili bir şekilde bir adrestir (ve ima yoluyla, adresi kendisidir ).
WhozCraig

7
Alıntı için "saygın kaynak" nedir?
Cornstalks

22
Nihai saygın kaynak dil standardıdır ve ondan yarı türetilmiş ve yazarın poposundan yarı çekilmiş kitaplar değildir. Bunu zor yoldan öğrendim, neredeyse yapabildiğim her hatayı yaptım ve yavaş yavaş standardın tarif ettiği modele biraz yakın bir zihinsel C modeli inşa ettim ve daha sonra adı geçen modeli standardın modeliyle değiştirdim.
Alexey Frunze

9
@thang İnsanlar işaretçi = tamsayı sanıyor çünkü çoğu zaman öyle (x86 Linux ve Windows bize "bunu öğretiyor"), çünkü insanlar genellemeyi seviyor, çünkü insanlar dil standardını iyi bilmiyorlar ve radikal olarak farklı bir deneyime sahip olmadıkları için platformlar. Aynı insanlar muhtemelen bir veri işaretçisi ve bir işlev işaretçisi birbirine dönüştürülebileceğini ve verilerin kod ve koda veri olarak erişilebildiğini varsayabilir. Bu von Neuman mimarileri için (1 adres alanı ile) doğru olabilir, ancak Harvard mimarileri (kod ve veri boşlukları ile) için geçerli olmayabilir.
Alexey Frunze

6
@exebook Standartlar yeni başlayanlar için değildir (özellikle tam olanlar). Nazik tanıtımlar ve çok sayıda örnek sunmaları beklenmemektedir. Resmi olarak bir şey tanımlarlar, böylece profesyoneller tarafından doğru bir şekilde uygulanabilir.
Alexey Frunze

Yanıtlar:


148

C standardı, işaretçinin dahili olarak ne olduğunu ve dahili olarak nasıl çalıştığını tanımlamaz. Bu, C'nin derlenmiş veya yorumlanmış bir dil olarak uygulanabileceği platform sayısını sınırlamamak için kasıtlıdır.

İşaretçi değeri, bir tür kimlik veya tanıtıcı veya birkaç kimliğin bir kombinasyonu olabilir (x86 segmentlerine ve ofsetlerine merhaba deyin) ve gerçek bir bellek adresi olmayabilir. Bu kimlik herhangi bir şey olabilir, sabit boyutlu bir metin dizesi bile. Adres dışı gösterimler özellikle bir C tercümanı için yararlı olabilir.


34
Açıklayacak çok şey yok. Her değişkenin adresi bellektir. Ancak adreslerini onlara işaret eden yerlerde saklamak zorunda değilsiniz. Bunun yerine değişkenlerinizi 1'den neye istediğinize kadar numaralandırabilir ve bu sayıyı işaretçiye kaydedebilirsiniz. Uygulama, bu sayıların adreslere nasıl dönüştürüleceğini ve bu sayılarla ve standardın gerektirdiği diğer tüm şeylerle işaretçi aritmetiğinin nasıl yapıldığını bildiği sürece, dil standardı başına tamamen yasaldır.
Alexey Frunze

4
i x86, bellek adresi bir segment seçici ve bir mahsup oluşur, bu yüzden bir işaretçi segment olarak temsil eder: ofset hala bellek adresini kullanıyor.
thang

6
@Lundin Standardı ve platformumu ve derleyicimi bildiğimde uygulanamaz olanın genel doğasını göz ardı etmekte sorun yaşamıyorum. Ancak orijinal soru geneldir, bu yüzden cevaplarken standardı görmezden gelemezsiniz.
Alexey Frunze

8
@Lundin Devrimci ya da bilim adamı olmanıza gerek yok. 32 bitlik bir makineyi fiziksel 16 bitlik bir makinede taklit etmek istediğinizi ve 64KB RAM'inizi disk depolama alanını kullanarak 4 GB'a kadar genişlettiğinizi ve 32 bit işaretçileri ofset olarak büyük bir dosyaya uyguladığınızı varsayalım. Bu işaretçiler gerçek bellek adresleri değil.
Alexey Frunze

6
Bunu şimdiye kadar gördüğüm en iyi örnek, Symbolics Lisp Machines için C uygulamasıydı (1990 dolaylarında). Her C nesnesi bir Lisp dizisi olarak uygulandı ve işaretçiler bir dizi ve bir dizin olarak uygulandı. Lisp'in dizi sınır denetimi nedeniyle, hiçbir zaman bir nesneden diğerine taşamazsınız.
Barmar

62

Kaynağınızdan emin değilim, ancak tanımladığınız dil türü C standardından geliyor:

6.5.3.2 Adres ve dolaylı operatörler
[...]
3. Tekli ve operatör, işleneninin adresini verir. [...]

Yani ... evet, işaretçiler bellek adreslerini gösteriyor. En azından C standardı bunu öneriyor.

Biraz daha açık söylemek gerekirse, bir işaretçi bazı adreslerin değerini tutan bir değişkendir . Bir nesnenin adresi (bir göstericide saklanabilir) tekli ile döndürülür& operatörle .

"42 Wallaby Way, Sydney" adresini bir değişkende saklayabilirim (ve bu değişken bir çeşit "işaretçi" olacaktır, ancak bu bir bellek adresi olmadığı için düzgün bir şekilde "işaretçi" dediğimiz bir şey değildir). Bilgisayarınızın bellek kovaları için adresleri vardır. İşaretçiler bir adresin değerini saklar (yani bir işaretçi bir adres olan "42 Wallaby Way, Sydney" değerini saklar).

Düzenle: Alexey Frunze'nin yorumunu genişletmek istiyorum.

İşaretçi tam olarak nedir? C standardına bakalım:

6.2.5 türleri
[...]
20. [...]
bir işaretçi tipi adı verilen bir fonksiyon tipi ya da bir nesne türü, türetilebilir Başvurulan tür . İşaretçi türü, değeri başvurulan türdeki bir varlığa başvuru sağlayan bir nesneyi tanımlar. Referans verilen T türünden türetilen bir işaretçi türüne bazen '' işaretçi T'ye '' denir. Başvuru yapılan bir türden bir işaretçi türünün oluşturulmasına `` işaretçi tür türevi '' denir. İşaretçi türü, tam bir nesne türüdür.

Temel olarak, işaretçiler bazı nesnelere veya işlevlere başvuru sağlayan bir değer depolar. Türü. İşaretçiler, bir nesneye veya işleve başvuru sağlayan bir değeri saklamak için tasarlanmıştır, ancak her zaman böyle değildir :

6.3.2.3 İşaretçiler
[...]
5. Bir tam sayı, herhangi bir işaretçi türüne dönüştürülebilir. Daha önce belirtilenler dışında, sonuç uygulama tanımlıdır, doğru şekilde hizalanmamış olabilir, başvurulan türde bir varlığa işaret etmeyebilir ve bir tuzak temsili olabilir.

Yukarıdaki alıntı, bir tamsayıyı bir işaretçiye dönüştürebileceğimizi söylüyor. Bunu yaparsak (yani, bir nesneye veya işleve belirli bir başvuru yerine bir işaretçiye bir tamsayı değeri doldurursak), işaretçi "başvuru türündeki bir varlığı göstermeyebilir" (yani, bir nesneye veya işleve gönderme). Bize başka bir şey sağlayabilir. Ve bu, bir işaretçiye bir tür tanıtıcı veya kimlik yapıştırabileceğiniz bir yerdir (yani işaretçi bir nesneye işaret etmiyor; bir şeyi temsil eden bir değeri saklıyor, ancak bu değer bir adres olmayabilir).

Evet, Alexey Frunze'nin dediği gibi, bir işaretçi bir nesneye veya işleve bir adres depolamıyor olabilir. Bunun yerine bir işaretçi bir çeşit "tanıtıcı" veya kimlik saklayabilir ve bunu bir işaretçiye rasgele bir tamsayı değeri atayarak yapabilirsiniz. Bu tanıtıcı veya kimliğin neyi temsil ettiği sistem / ortama / bağlama bağlıdır. Sisteminiz / uygulamanız değeri anlayabildiği sürece, iyi durumdasınızdır (ancak bu, belirli bir değere ve belirli bir sisteme / uygulamaya bağlıdır).

Normalde , bir işaretçi bir adresi bir nesneye veya işleve depolar. Gerçek bir adresi (bir nesneye veya işleve) saklamıyorsa, sonuç uygulama tanımlıdır (yani tam olarak ne olduğu ve imlecin şimdi neyi temsil ettiği sisteminize ve uygulamanıza bağlıdır, bu nedenle bir tanıtıcı veya kimlik olabilir ancak belirli bir sistemde aynı kodu / değeri kullanmak programınızı kilitleyebilir).

Bu, düşündüğümden daha uzun oldu ...


3
C yorumlayıcısında, bir işaretçi adres olmayan bir kimlik / tanıtıcı / vb. İçerebilir.
Alexey Frunze

4
@exebook Standart herhangi bir şekilde derlenmiş C ile sınırlı değildir
Alexey Frunze

7
@Lundin Bravo! Standardı daha fazla görmezden gelelim! Sanki zaten yeterince görmezden gelmedik ve bu nedenle buggy ve zayıf taşınabilir yazılım üretmedik. Ayrıca, orijinal sorunun genel olduğunu ve bu nedenle genel bir cevaba ihtiyaç duyduğunu lütfen unutmayın.
Alexey Frunze

3
Diğerleri bir işaretçinin tanıtıcı veya adres dışında başka bir şey olabileceğini söylediklerinde, yalnızca işaretçiyi bir tamsayıyı göstererek bir işaretçiye zorlayabileceğiniz anlamına gelmez. Derleyicinin işaretçileri uygulamak için bellek adresleri dışında bir şey kullanıyor olabileceği anlamına gelir. DEC'ın ABI'sı olan Alpha işlemcisinde, bir işlev işaretçisi işlevin adresi değil, bir işlevin tanımlayıcısının adresiydi ve tanımlayıcı işlevin adresini ve işlev parametreleriyle ilgili bazı verileri içeriyordu. Mesele şu ki C standardı çok esnektir.
Eric Postpischil

5
@Lundin: İşaretçilerin gerçek dünyadaki mevcut bilgisayar sistemlerinin% 100'ünde tamsayı adresleri olarak uygulandığı iddiası yanlıştır. Bilgisayarlar kelime adresleme ve segment ofseti adresleme ile mevcuttur. Derleyiciler hala yakın ve uzak göstergeleri destekliyor. PDP-11 bilgisayarlar, RSX-11 ve Görev Oluşturucu ve bindirmeleri ile birlikte bulunur; burada bir işaretçinin bir işlevi diskten yüklemek için gereken bilgileri tanımlaması gerekir. Nesne bellekte değilse, bir işaretçi bir nesnenin bellek adresine sahip olamaz!
Eric Postpischil

39

İşaretçi ve Değişken

Bu resimde,

pointer_p 0x12345 adresinde bulunan ve 0x34567 değişken_v değişkenine işaret eden bir işaretçi.


16
Bu, yalnızca işaretçinin aksine adres kavramını ele almakla kalmaz, aynı zamanda bir adresin sadece bir tamsayı olmadığı noktasını bütünüyle özler.
Gilles 'SO- kötü'

19
-1, bu sadece bir işaretçinin ne olduğunu açıklar. O sorum değildi ve bir kenara soru tüm karmaşıklığını itiyorsun olduğu hakkında.
alexis

34

Bir işaretçiyi adres olarak düşünmek bir yaklaşımdır . Tüm yaklaşımlar gibi, bazen yararlı olacak kadar iyidir, ancak aynı zamanda kesin değildir, bu da ona güvenmenin belaya neden olduğu anlamına gelir.

İşaretçi, bir nesnenin nerede bulunacağını belirten bir adres gibidir. Bu benzetmenin hemen bir sınırlaması, tüm işaretçilerin aslında bir adres içermemesidir. NULLadres olmayan bir işaretçi. Bir işaretçi değişkeninin içeriği aslında üç türden biri olabilir:

  • Adres duruma gelmiş olabilir, bir nesnenin (eğer padresini ihtiva eden xdaha sonra ifade *paynı değere olarak bulunur x);
  • bir örnek olan bir boş göstericiNULL ;
  • bir nesneye işaret etmeyen geçersiz içerik ( pgeçerli bir değere sahip değilse *p, programın oldukça yaygın bir olasılıkla çökmesine neden olan herhangi bir şey (“tanımsız davranış”) yapabilir).

Ayrıca, bir işaretçinin (geçerli ve boş değilse) bir adres içerdiğini söylemek daha doğru olur : bir işaretçi bir nesnenin nerede bulunacağını gösterir, ancak ona bağlı daha fazla bilgi vardır.

Özellikle, işaretçinin bir türü vardır. Çoğu platformda, işaretçinin türünün çalışma zamanında bir etkisi yoktur, ancak derleme zamanında türün ötesine geçen bir etkisi vardır. Eğer pbir işaretçidir int( int *p;), daha sonra p + 1bir tam sayı için nokta sizeof(int)sonra bayt p(varsayarak p + 1hala geçerli bir işaretçidir). Bu qöğeye bir işaretçi ( ) ile charaynı adrese işaret ediyorsa , aynı adresle aynı değildir . İşaretçiyi adres olarak düşünüyorsanız, “sonraki adres” in aynı konuma farklı işaretçiler için farklı olması çok sezgisel değildir.pchar *q = p;q + 1p + 1

Bazı ortamlarda, bellekte aynı konuma işaret eden farklı gösterimlere (bellekte farklı bit desenleri) sahip birden fazla işaretçi değerine sahip olmak mümkündür. Bunları aynı adrese sahip farklı işaretçiler veya aynı konum için farklı adresler olarak düşünebilirsiniz - bu durumda metafor net değildir. ==İki işlenen nedenle sahip olabilir bu ortamlarda, aynı konuma işaret olup olmadığını operatör hep söyler p == qrağmen pve qfarklı bit desenleri var.

İşaretçilerin adresin ötesinde tür veya izin bilgileri gibi başka bilgiler taşıdığı ortamlar bile vardır. Bunlarla karşılaşmadan bir programcı olarak hayatınızdan kolayca geçebilirsiniz.

Farklı tür işaretçilerin farklı temsillere sahip olduğu ortamlar vardır. Bunu farklı temsillere sahip farklı adres türleri olarak düşünebilirsiniz. Örneğin, bazı mimarilerde bayt işaretçileri ve sözcük işaretçileri veya nesne işaretçileri ve işlev işaretçileri bulunur.

Sonuç olarak, işaretçileri adres olarak düşünmek,

  • yalnızca geçerli, boş olmayan işaretçiler adreslerdir;
  • aynı konum için birden fazla adresiniz olabilir;
  • adreslerde aritmetik yapamazsınız ve adreslerde düzen yoktur;
  • işaretçi de tip bilgisi taşır.

Diğer yöne gitmek çok daha zahmetlidir. Adrese benzeyen her şey bir işaretçi olamaz . Derinlemesine bir yerde herhangi bir işaretçi bir tamsayı olarak okunabilen bir bit deseni olarak temsil edilir ve bu tamsayının bir adres olduğunu söyleyebilirsiniz. Ama diğer taraftan, her tam sayı bir işaretçi değildir.

İlk önce bazı iyi bilinen sınırlamalar vardır; örneğin, programınızın adres alanının dışındaki bir konumu belirten bir tam sayı geçerli bir işaretçi olamaz. Yanlış hizalanmış bir adres, hizalama gerektiren bir veri türü için geçerli bir işaretçi oluşturmaz; örneğin, int4 baytlık hizalama gerektiren bir platformda 0x7654321 geçerli bir int*değer olamaz .

Ancak, bunun ötesine geçer, çünkü bir tamsayıya bir işaretçi yaptığınızda, sorun dünyasına girersiniz. Bu sorunun büyük bir kısmı, derleyicileri optimize etmenin mikrooptimizasyonda çoğu programcının beklediğinden çok daha iyi olmasıdır, böylece bir programın nasıl çalıştığına dair zihinsel modelleri derinden yanlıştır. Aynı adrese sahip işaretçileriniz olması, eşdeğer oldukları anlamına gelmez. Örneğin, aşağıdaki snippet'i düşünün:

unsigned int x = 0;
unsigned short *p = (unsigned short*)&x;
p[0] = 1;
printf("%u = %u\n", x, *p);

Bunu , sizeof(int)==4ve ( sizeof(short)==2ya 1 = 1?küçük endian) ya da 65536 = 1?(big-endian) baskı yapan bir değirmen makinesinde bekleyebilirsiniz . Ancak GCC 4.4 yüklü 64 bit Linux bilgisayarımda:

$ c99 -O2 -Wall a.c && ./a.out 
a.c: In function main’:
a.c:6: warning: dereferencing pointer p does break strict-aliasing rules
a.c:5: note: initialized from here
0 = 1?

GCC, bu basit örnekte neyin yanlış gittiğini bize uyaracak kadar nazik - daha karmaşık örneklerde derleyici fark etmeyebilir. Yana pfarklı bir türü olan &xneyi değişen, pneyi etkileyemez işaret &x(dışında bazı iyi tanımlanmış istisnalarla) puan. Bu nedenle derleyici, xbir kayıttaki değerini tutma ve bu kaydı *pdeğişiklik olarak güncelleştirmeme özgürlüğüne sahiptir . Program, iki adresin aynı adrese gönderilmesini engeller ve iki farklı değer elde eder!

Bu örneğin ahlaki, C dilinin kesin kuralları dahilinde kaldığınız sürece (boş olmayan) bir işaretçiyi adres olarak düşünmenin iyi olmasıdır. Madalyonun çevirme tarafı, C dilinin kurallarının karmaşık ve kaputun altında ne olduğunu bilmiyorsanız sezgisel bir his elde etmek zor olmasıdır. Ve kaputun altında olan şey, hem "egzotik" işlemci mimarilerini desteklemek ve derleyicileri optimize etmek için işaretçiler ve adresler arasındaki bağlantının biraz gevşek olmasıdır.

Bu nedenle, işaretçilerin adresleri anlayışınızdaki ilk adım olarak düşünün, ancak bu sezgiyi çok fazla takip etmeyin.


5
+1. Diğer cevaplar bir işaretçinin tür bilgisi ile birlikte geldiğini kaçırıyor gibi görünüyor. Bu, adres / kimlik / her türlü tartışmadan çok daha önemlidir.
undur_gongor

+1 Tip bilgisi hakkında mükemmel noktalar. Derleyici örneklerinin doğru olduğundan emin değilim ... Örneğin, *p = 3p başlatılmadığında başarılı olması çok olası görünmüyor .
LarsH

@LarsH Haklısın, teşekkürler, bunu nasıl yazdım? Bilgisayarımdaki şaşırtıcı davranışı bile gösteren bir örnekle değiştirdim.
Gilles 'SO- kötü

1
hm, NULL ((geçersiz *) 0) ..?
Aniket Inge

1
@ gnasher729 boş işaretçisi olan bir işaretçi. NULLdeğil, ama burada gerekli ayrıntı seviyesi için, bu alakasız bir dikkat dağıtıcı. Günlük programlama için bile, NULL“işaretçi” NULLdemeyen bir şey olarak uygulanabileceği gerçeği sık sık ortaya çıkmaz (öncelikle değişken bir işleve geçer - ancak orada bile yapmıyorsanız , zaten tüm işaretçi türlerinin aynı temsile sahip olduğunu varsayıyorsunuz).
Gilles 'SO- kötü olmayı kes

19

İşaretçi, adresin kendisini değil, HOLDS bellek adresini içeren bir değişkendir. Bununla birlikte, bir işaretçiyi devre dışı bırakabilir ve bellek konumuna erişebilirsiniz.

Örneğin:

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

Bu kadar. Bu kadar basit.

resim açıklamasını buraya girin

Ne dediğimi ve çıktısını göstermek için bir program burada:

http://ideone.com/rcSUsb

Program:

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}

5
Daha da karışık olabilir. Alice, bir kedi görebiliyor musun? Hayır, sadece bir kedinin gülümsemesini görebiliyorum. İşaretçi bir adres veya işaretçi bir adres tutan bir değişken ya da işaretçi bir adres fikri anlamına gelen bir kavramın adı olduğunu söylemek, kitap yazarları neewewies karıştırmak ne kadar ileri gidebilir?
exebook

@exers işaretçiler terbiyeli, oldukça basit. Belki bir resim yardımcı olur?
Aniket Inge

5
İşaretçinin bir adresi olması gerekmez. Bir C tercümanında, başka bir şey, bir çeşit kimlik / işlem olabilir.
Alexey Frunze

"Etiket" veya değişken adı bir derleyici / montajcı ve makine düzeyinde mevcut değil, bu yüzden bellekte görünmesi gerektiğini sanmıyorum.
Ben

1
@Aniket İşaretçi değişkeni bir işaretçi değeri içerebilir. fopenBir değişkeni yalnızca bir kereden fazla kullanmanız gerektiğinde (bir değişkeni saklamanız yeterlidir fopen).
Gilles 'SO- kötü olmayı kes'

16

Bu kitapların yazarlarının tam olarak ne anlama geldiğini söylemek zor. İşaretçinin adres içerip içermediği, bir adresi nasıl tanımladığınıza ve işaretçiyi nasıl tanımladığınıza bağlıdır.

Yazılan tüm cevaplara bakılırsa, bazı insanlar (1) bir adresin bir tamsayı olması gerektiğini ve (2) bir işaretçinin belirtmede belirtilmeyen sanal olarak olması gerekmediğini varsayar. Bu varsayımlarla, açıkça işaretçiler mutlaka adres içermez.

Ancak görüyoruz ki (2) muhtemelen doğru, (1) muhtemelen doğru olmak zorunda değildir. Ve & @ CornStalks'ın cevabına göre operatörün adresi olarak adlandırıldığından ne yapmalı? Bu, belirtimdeki yazarların bir işaretçinin adres içermesi gerektiği anlamına mı geliyor?

Peki, işaretçinin bir adres içerdiğini söyleyebiliriz, ancak bir adresin bir tam sayı olması gerekmez mi? Olabilir.

Bence tüm bunlar cılız bilgiçliksel semantik konuşma. Pratik olarak konuşmaya değmez. İşaretçinin değeri bir adres olmayacak şekilde kod üreten bir derleyici düşünebilir misiniz? Öyleyse ne olmuş? Bende böyle düşünmüştüm...

Kitabın yazarının (işaretçilerin mutlaka sadece adresler olmadığını iddia eden ilk alıntı) muhtemelen bahsettiği şey, bir işaretçinin kendisiyle birlikte gelen tür bilgisiyle birlikte gelmesidir.

Örneğin,

 int x;
 int* y = &x;
 char* z = &x;

y ve z'nin her ikisi de işaretçilerdir, ancak y + 1 ve z + 1 farklıdır. eğer bunlar bellek adresleri ise, bu ifadeler size aynı değeri vermez mi?

Ve burada yalancıların adresler gibi düşünmesi genellikle kedere yol açar . Hatalar yazılmıştır, çünkü insanlar işaretçileri adresmiş gibi düşünürler ve bu genellikle kedere yol açar .

55555 muhtemelen bir işaretçi değildir, ancak bir adres olabilir, ancak (int *) 55555 bir işaretleyicidir. 55555 + 1 = 55556, ancak (int *) 55555 + 1 55559'dur (sizeof (int) cinsinden +/- fark).


1
İşaretçi aritmetiğine işaret eden +1, adreslerdeki aritmetik ile aynı değildir.
kutschkem

16-bit 8086 durumunda, bir bellek adresi bir segment tabanı + ofseti ile tanımlanır, her ikisi de 16 bit. Bellekte aynı adresi veren birçok segment tabanı + ofseti kombinasyonu vardır. Bu farişaretçi yalnızca "bir tam sayı" değildir.
vonbrand

@vonbrand neden bu yorumu gönderdiğini anlamıyorum. bu konu diğer cevaplar altında yorumlar olarak tartışılmıştır. hemen hemen her diğer yanıt adres = tamsayı olduğunu ve tamsayı olmayan her şeyin adres olmadığını varsayar. Ben sadece bu işaret ve doğru olabilir ya da olmayabilir. cevabımdaki bütün mesele bunun ilgili olmadığı. hepsi bilgiçlik taslayan, ve asıl mesele diğer cevaplarda ele alınmıyor.
thang

@tang, "pointer == adres" fikri yanlış . Herkesin ve en sevdiği teyzenin söylemeye devam etmesi bunu doğru yapmaz.
vonbrand

@vonbrand ve bu yorumu neden yayınım altında yaptınız? Doğru ya da yanlış olduğunu söylemedim. Aslında, bazı senaryolarda / varsayımlarda doğrudur, ancak her zaman değil. Tekrar yazının noktasını özetleyeyim (ikinci kez). cevabımdaki bütün mesele bunun ilgili olmadığı. hepsi bilgiçlik taslayan, ve asıl mesele diğer cevaplarda ele alınmıyor. pointer == adres veya address == tamsayı iddiasında bulunan cevaplar hakkında yorum yapmak daha uygun olacaktır. segment: ofset ile ilgili olarak Alexey'nin gönderisi altındaki yorumlarıma bakın.
thang

15

İşaretçi, bir bellek konumunu temsil eden bir soyutlamadır . Alıntı, işaretçiler hakkında sanki bellek adresleri gibi düşünmenin yanlış olduğunu söylemediğini, sadece "genellikle kedere yol açtığını" söylüyor. Başka bir deyişle, yanlış beklentilere sahip olmanıza yol açar.

En büyük keder kaynağı kesinlikle C'nin güçlü yönlerinden biri olan işaretçi aritmetiğidir . Bir işaretçi bir adresse, işaretçi aritmetiğinin adres aritmetiği olmasını beklersiniz; ama değil. Örneğin, bir adrese 10 eklenmesi size 10 adresleme birimi tarafından daha büyük bir adres vermelidir; ancak bir işaretçiye 10 eklenmesi, işaret ettiği nesne türünün boyutunun 10 katı artar (ve gerçek boyutta bile değil, ancak bir hizalama sınırına yuvarlanır). Bir ile int *40 ele birimi (bayt) ile bir artıracaktır olur için 10 ilave bir 32-bit tamsayı olan sıradan bir mimariye. Deneyimli C programcıları bunun farkında ve onunla yaşıyorlar, ancak yazarınız açıkça özensiz metaforların hayranı değil.

İşaretçinin içeriğinin bellek konumunu nasıl temsil ettiğine dair ek soru vardır : Cevapların çoğunun açıkladığı gibi, bir adres her zaman int (veya uzun) değildir. Bazı mimarilerde bir adres bir "segment" artı bir ofsettir. Bir işaretçi yalnızca kendi başına benzersiz bir bellek adresi olmayan geçerli segmentin ("yakın" işaretçisi) ofsetini bile içerebilir. İşaretçi içeriğinin, donanımın anladığı gibi bellek adresiyle yalnızca dolaylı bir ilişkisi olabilir. Ancak alıntı yapılan alıntıdan, temsilden bile bahsetmiyoruz, bu yüzden akıllarında temsilden ziyade kavramsal eşdeğerlik olduğunu düşünüyorum.


12

Geçmişte bazı karışık insanlara şu şekilde açıkladım: Bir işaretçinin davranışını etkileyen iki özelliği vardır. Bir bellek adresi olan bir değere (tipik ortamlarda) ve işaret ettiği nesnenin türünü ve boyutunu söyleyen bir türe sahiptir.

Örneğin, verilenler:

union {
    int i;
    char c;
} u;

Aynı nesneyi gösteren üç farklı işaretçiniz olabilir:

void *v = &u;
int *i = &u.i;
char *c = &u.c;

Bu işaretçilerin değerlerini karşılaştırırsanız, hepsi eşittir:

v==i && i==c

Ancak, her bir işaretçiyi artırırsanız, işaret ettikleri türün alakalı hale geldiğini görürsünüz .

i++;
c++;
// You can't perform arithmetic on a void pointer, so no v++
i != c

Değişkenler ive cçünkü bu noktada farklı değerlere sahip olacaktır i++nedenleri isonraki erişimli tamsayı adresi bulunması ve c++neden cgelecek adreslenebilir karaktere noktaya kadar. Genellikle, tamsayılar karakterlerden daha fazla bellek kaplar, bu nedenle her ikisi de arttıkça iolduğundan daha büyük bir değerle sonuçlanır c.


2
+1 Teşekkürler. İşaretçilerle, değer ve tür, insanın bedenini ruhundan ayırabileceği kadar ayrılmazdır.
Aki Suihkonen

i == ckötü biçimlendirilmiş (yalnızca birinden diğerine örtük dönüşüm varsa işaretçileri farklı türlerle karşılaştırabilirsiniz). Ayrıca, bunu bir dökümle düzeltmek, bir dönüşüm uyguladığınız anlamına gelir ve daha sonra dönüşümün değeri değiştirip değiştirmediği tartışmalıdır. (Bunun olmadığını iddia edebilirsiniz, ancak bu sadece bu örnekle kanıtlamaya çalıştığınız şeyi iddia ediyor).
MM

8

Mark Bessey zaten söyledi, ama anlaşılana kadar bunun yeniden vurgulanması gerekiyor.

İşaretçinin bir değişmez değerle ilgisi vardır 3.

İşaretçi , bir değerin (bir adresin) ve bir türün demetidir (salt okunur gibi ek özelliklere sahip). Tür (ve varsa ek parametreler) içeriği daha fazla tanımlayabilir veya kısıtlayabilir; Örneğin. __far ptr, __near ptr: adresin içeriği nedir: yığın, yığın, doğrusal adres, bir yerden ofset, fiziksel bellek veya ne.

İşaretçi aritmeti tamsayı aritmetiğinden biraz farklı yapan türün özelliğidir .

Değişken olmama işaretçisinin karşı örnekleri göz ardı edilemeyecek kadar çok

  • DOSYA işaretçisi dönen fopen. (değişken nerede)

  • yığın işaretçisi veya genellikle işaretlenemeyen kayıtlar olan çerçeve işaretçisi

    *(int *)0x1231330 = 13; - pointer_of_integer türüne rastgele bir tam sayı değeri vermek ve bir değişken girmeden bir tam sayı yazmak / okumak

Bir C-programının ömrü boyunca, adresleri olmayan geçici işaretçilerin başka örnekleri de olacaktır ve bu nedenle değişkenler değil, derleme zamanı ile ilişkili türdeki ifadeler / değerlerdir.


8

Haklısın ve aklı başındasın. Normalde, bir işaretçi sadece bir adrestir, böylece tamsayıya atabilir ve herhangi bir aritmetik yapabilirsiniz.

Ancak bazen işaretçiler bir adresin sadece bir parçasıdır. Bazı mimarilerde bir işaretçi, baz veya başka bir CPU eklenerek bir adrese dönüştürülür kaydı kullanılır.

Ancak bu gün, düz bellek modeli ve C dili yerel olarak derlenmiş PC ve ARM mimarisinde, bir işaretçinin tek boyutlu adreslenebilir RAM'daki bir yere tamsayı bir adres olduğunu düşünmek sorun değil.


PC ... düz bellek modeli? seçiciler nedir?
thang

Riight. Ve bir sonraki mimari değişiklik ortaya çıktığında, belki de ayrı kod ve veri alanlarıyla ya da birileri saygıdeğer segment mimarisine geri döndüğünde (güvenlik için tonlarca mantıklıdır, hatta izinleri kontrol etmek için segment numarası + ofsetine bir miktar anahtar ekleyebilir) güzel "işaretçiler sadece tamsayılar" çöküyor geliyor.
vonbrand

7

Bir işaretçi, C'deki diğer herhangi bir değişken gibi, temel olarak bir veya daha fazla birleştirilmiş unsigned chardeğerle temsil edilebilen bir bit topluluğudur ( diğer herhangi bir bakım türünde olduğu gibi, değerlerin sizeof(some_variable)sayısını gösterecektir unsigned char). İşaretçiyi diğer değişkenlerden farklı kılan şey, bir C derleyicisinin bir işaretçi içindeki bitleri bir şekilde bir değişkenin depolanabileceği bir yer olarak tanımlamasıdır. C'de, diğer bazı dillerden farklı olarak, birden çok değişken için yer talep etmek ve daha sonra bir işaretçiyi bu kümedeki herhangi bir değere bir işaretçiye o kümedeki herhangi bir diğer değişkene dönüştürmek mümkündür.

Birçok derleyici, bitlerini kullanarak gerçek makine adreslerini depolayarak işaretçiler uygular, ancak bu mümkün olan tek uygulama değildir. Bir uygulama, bir programın kullandığı tüm bellek nesnelerinin (değişken kümeleri) donanım adresini ve ayrılan boyutunu listeleyen - kullanıcı kodu tarafından erişilemeyen - bir dizi tutabilir ve her işaretçinin bir diziye bir dizin içermesini sağlayabilir bu dizinden bir ofset ile. Böyle bir tasarım, bir sistemin yalnızca sahip olduğu bellekle çalışmasını kodla sınırlamakla kalmaz, aynı zamanda bir bellek öğesinin işaretçisinin yanlışlıkla başka bir bellek öğesine (donanım kullanan bir sistemde) bir işaretçiye dönüştürülememesini sağlar. adresleri, eğer foove yerine ilk öğeye işaret olabilirbar art arda bellekte saklanan 10 öğeden oluşan dizilerse,foobar, ancak her "işaretçinin" bir nesne kimliği ve ofset olduğu bir sistemde, kod bir işaretçiyi fooayrılan aralığın ötesine endekslemeye çalıştığında sistem bindirebilir ). Herhangi bir işaretçi ile ilişkili fiziksel adresler hareket ettirilebildiğinden, böyle bir sistemin bellek parçalanması sorunlarını ortadan kaldırması da mümkündür.

İşaretçiler bir şekilde soyut olsa da, tam standartlara uygun bir C derleyicisinin bir çöp toplayıcıyı uygulamasına izin verecek kadar soyut olmadığını unutmayın. C derleyicisi, işaretçiler de dahil olmak üzere her değişkenin bir depolama dizisi olarak temsil edildiğini (ona bir işaretçi alarak), orada bir şeyler depoladığını, işaretçiyi bir dizi bayt halinde ayrıştırdığını, bunları ekranda görüntülediğini ve ardından tümünü sildiğini belirtir. onlara referans. Program daha sonra klavyeden bazı sayıları kabul ettiyse, bunları bir işaretçiye yeniden oluşturduysa ve daha sonra bu işaretçiden veri okumaya çalıştıysa ve kullanıcı programın daha önce görüntülediği aynı sayıları girerse, programın verileri çıkarması gerekir. içinde depolanmış olanunsigned char değerler . Herhangi bir değişken göz önüne alındığında, bir sayı dizisine ayrılabilir ve daha sonra bu sayı dizisini orijinal tipteki bir değişkene geri dönüştürebilir. Sonuç olarak, bir programıncalloccallocbellek. Bilgisayarın kullanıcının görüntülenen sayıların bir kopyasını yapıp yapmadığını bilmesi imkansız bir yol olmadığından, bilgisayar yukarıda belirtilen belleğe gelecekte erişilip erişilemeyeceğini bilemeyebilir.


Büyük bir yükte, sayısal değerini "sızdırabilecek" işaretçi değerinin herhangi bir kullanımını tespit edebilir ve çöp toplayıcının onu toplamayacak veya yerini değiştirmeyecek şekilde ayırmayı sabitleyebilirsiniz free(tabii ki açıkça çağrılmadığı sürece ). Elde edilen uygulamanın bu kadar faydalı olup olmayacağı başka bir konudur, çünkü toplama yeteneği çok sınırlı olabilir, ancak en azından bir çöp toplayıcı diyebilirsiniz :-) İşaretçi ataması ve aritmetik değeri "sızdırmaz", ancak char*kaynağı bilinmeyen herhangi bir erişimin kontrol edilmesi gerekir.
Steve Jessop

@SteveJessop: Sanırım böyle bir tasarım işe yaramazdan daha kötü olurdu, çünkü kodun hangi işaretçilerin serbest bırakılması gerektiğini bilmesi imkansız olurdu. Bir işaretçi gibi görünen herhangi bir şeyi üstlenen muhafazakârlar, aşırı muhafazakar olabilir, ancak genellikle işaretçiler gibi görünen şeylerin değişme olasılığı vardır, böylece "kalıcı" bellek sızıntılarından kaçınırlar. İşaretçiyi kalıcı olarak dondurmak için bir işaretçiyi ayrıştırıyor gibi görünen herhangi bir eylemin olması, bellek sızıntıları için garantili bir reçetedir.
Supercat

Performans nedenlerinden dolayı yine de başarısız olacağını düşünüyorum - eğer her erişim kontrol çünkü kod yavaş yavaş çalıştırmak istiyorsanız o zaman C ;-) yazmayın C programcıların yaratıcılığı için sizden daha yüksek umutları var, zahmetli olsa da, tahsisleri gereksiz yere sabitlemekten kaçınmak muhtemelen imkansız değildir. Her neyse, C ++, bu sorunla başa çıkmak için "güvenli bir şekilde türetilen işaretçiler" i tam olarak tanımlar, bu nedenle C işaretçilerin soyutluğunu makul düzeyde etkili çöp toplamalarını destekleyecek düzeye yükseltmek istiyorsak ne yapacağımızı biliyoruz.
Steve Jessop

@SteveJessop: Bir GC sisteminin yararlı olması için, freeçağrılmamış belleği güvenilir bir şekilde serbest bırakabilmeli veya serbest bırakılmış bir nesneye yapılan herhangi bir başvurunun canlı bir nesneye referans olmasını engelleyebilmelidir [gerektiren kaynakları kullanırken bile açık yaşam boyu yönetimi, GC yine de ikinci işlevi yerine getirebilir]; N nesnelerinin aynı anda gereksiz yere sabitlenmesi olasılığı N büyüdükçe sıfıra yaklaştığında bazen nesneleri hatalı şekilde canlı referanslar olarak gören bir GC sistemi kullanılabilir . Bir derleyici hatası işaretlemek istemiyorsa ...
Supercat

... geçerli C ++, ancak derleyici bir işaretçi asla tanınmayan bir forma dönüştürülemez olduğunu kanıtlayamaz kod için, nasıl bir program aslında hiç bir zaman risk önlemek nasıl görmüyorum işaretçileri tamsayı olarak kullanır, hatalı olarak kabul edilebilir.
Supercat

6

İşaretçi, C / C ++ 'da yerel olarak bulunan ve bir bellek adresi içeren değişken bir tiptir. Diğer tüm değişkenler gibi kendine ait bir adresi vardır ve belleği kaplar (miktar platforma özgüdür).

Karışıklığın bir sonucu olarak göreceğiniz bir sorun, işaretçiyi değere geçirerek bir işlev içindeki başvuruyu değiştirmeye çalışmaktır. Bu, işlev kapsamındaki imlecin bir kopyasını oluşturur ve bu yeni imlecin "işaret ettiği" yerde yapılan herhangi bir değişiklik, işlevi çağıran kapsamdaki imlecin başvurusunu değiştirmez. Bir işlev içindeki gerçek işaretçiyi değiştirmek için normalde bir işaretçiyi bir işaretçiye geçirir.


1
Genellikle, bir tanıtıcı / kimlik. Genellikle, açık bir adres.
Alexey Frunze

Cevabımı wikipedia'daki Handle tanımına biraz daha PC olacak şekilde ayarladım . Bir tanıtıcı bir işaretçi bir referans olabilir, çünkü işaretçiler bir tanıtıcı belirli bir örneği olarak başvurmak istiyorum.
Matthew Sanders

6

KISA ÖZET (ki ben de en üste koyacağım):

(0) İşaretçilerin adres olarak düşünülmesi genellikle iyi bir öğrenme aracıdır ve genellikle sıradan veri türlerine işaretçiler için gerçek bir uygulamadır.

(1) Ancak, çoğu, belki de çoğu, işlevlere yönelik işaretçiler adres değildir, ancak bir adresten daha büyüktür (genellikle 2x, bazen daha fazla) veya aslında işlev ve benzeri adresleri içeren bellekteki bir yapıya işaretçilerdir. sabit bir havuz.

(2) Veri üyelerine ve yöntemlere işaretçiler genellikle daha da yabancıdır.

(3) FAR ve YAKIN işaretçi sorunları olan eski x86 kodu

(4) Güvenli "yağ işaretçileri" olan birçok örnek, özellikle IBM AS / 400.

Eminim daha fazlasını bulabilirsin.

DETAY:

UMMPPHHH !!!!! Şimdiye kadar cevapların çoğu oldukça tipik "programcı weenie" cevapları - ama derleyici weenie veya donanım weenie değil. Bir donanım weenie gibi davrandığım ve genellikle derleyici weenies ile çalıştığım için, iki sentimi atayım:

Pek çok, muhtemelen çoğu, C derleyicilerinde, türdeki verilere bir işaretçi Taslında adresidir T.

İnce.

Ancak, bu derleyicilerin çoğunda bile, belirli işaretçiler adres DEĞİLDİR. Bunu bakarak anlatabilirsiniz sizeof(ThePointer).

Örneğin, işlevlere yönelik işaretçiler bazen sıradan adreslerden çok daha büyüktür. Veya bir miktar dolaylılık içerebilirler. Bu makaleIntel Itanium işlemcisini içeren bir açıklama sağlar, ancak başkalarını gördüm. Tipik olarak, bir fonksiyonu çağırmak için sadece fonksiyon kodunun adresini değil, aynı zamanda fonksiyonun sabit havuzunun adresini de bilmelisiniz - sabitleyicilerin oluşturmak zorunda olduğu derleyiciden ziyade sabitlerin tek bir yükleme talimatı ile yüklendiği bir bellek bölgesi Çeşitli Yük Anında ve Üst Karakter ve OR komutlarında 64 bit sabit. Yani, tek bir 64 bit adres yerine 2 64 bit adrese ihtiyacınız var. Bazı ABI'ler (Uygulama İkili Arabirimleri) bunu 128 bit olarak hareket ettirirken, diğerleri işaretçi aslında bir dolaylılık düzeyi kullanır, işlev işaretçisi aslında belirtilen 2 gerçek adresi içeren bir işlev tanımlayıcısının adresidir. Hangisi daha iyi? Bakış açınıza bağlıdır: performans, kod boyutu, ve bazı uyumluluk sorunları - kod genellikle bir işaretçinin uzun veya uzun bir uzunluğa dönüştürülebileceğini varsayar, ancak uzun uzunluğun tam olarak 64 bit olduğunu varsayabilir. Bu tür kodlar standartlara uygun olmayabilir, ancak yine de müşteriler bu kodun çalışmasını isteyebilir.

Birçoğumuz YAKIN POINTER ve FAR POINTERS ile eski Intel x86 segmentli mimarinin acı dolu anılarına sahibiz. Neyse ki bunlar şu ana kadar neredeyse tükenmiş durumda, bu yüzden sadece hızlı bir özet: 16 bit gerçek modda, gerçek doğrusal adres

LinearAddress = SegmentRegister[SegNum].base << 4 + Offset

Korunmuş moddayken,

LinearAddress = SegmentRegister[SegNum].base + offset

sonuçta ortaya çıkan adres, segmentte belirlenen bir sınıra göre kontrol edilir. Bazı programlar gerçekten standart C / C ++ FAR ve YAKIN işaretçi bildirimleri kullanmamıştır, ancak birçoğu sadece *T--- derleyici ve bağlayıcı anahtarları olduğunu söyledi, örneğin, kod işaretçileri işaretçilerin yakınında olabilir, her şeye karşı sadece 32 bit ofset olabilir CS (Kod Segmenti) kaydı, veri işaretçileri 48 bit değer için hem 16 bit segment numarası hem de 32 bit ofset belirterek FAR işaretçileri olabilir. Şimdi, bu miktarların her ikisi de kesinlikle adresle ilişkilidir, ancak aynı boyutta olmadıklarından hangisi adresdir? Ayrıca, segmentler, gerçek adresle ilgili öğelere ek olarak, salt okunur, okuma-yazma, yürütülebilir - izinler de taşıdı.

Daha ilginç bir örnek olan IMHO, IBM AS / 400 ailesidir (veya belki de öyleydi). Bu bilgisayar, C ++ 'da işletim sistemi uygulayan ilk bilgisayarlardan biriydi. Bu makinedeki işaretçiler tipik olarak gerçek adres boyutunun 2 katı idi - ör. bu sunumda128 bit işaretçiler, ancak gerçek adresler 48-64 bit ve yine, okuma, yazma gibi izinlerin yanı sıra arabellek taşmasını önleme sınırı sağlayan bazı ekstra bilgilerdi. Evet: bunu C / C ++ ile uyumlu bir şekilde yapabilirsiniz - ve eğer her yerde bu olsaydı, Çin PLA'sı ve Slav mafyası pek çok Batı bilgisayar sistemine girmeyecekti. Ancak tarihsel olarak çoğu C / C ++ programlaması performans güvenliğini ihmal etmiştir. En ilginç olanı, AS400 ailesi işletim sisteminin, ayrıcalıksız koda verilebilen, ancak ayrıcalıklı olmayan kodun sahte veya kurcalanamadığı güvenli işaretçiler oluşturmasına izin verdi. Yine, güvenlik ve standartlarla uyumlu olsa da, standartlarla uyumlu çok özensiz C / C ++ kodu böyle güvenli bir sistemde çalışmaz. Yine resmi standartlar var,

Şimdi, güvenlik sabun kutumdan çıkacağım ve işaretçilerin (çeşitli türlerde) genellikle gerçekte adres olmadığı başka yollardan bahsedeceğim: Veri üyelerine işaretçiler, üye işlev yöntemlerine işaretçiler ve bunların statik sürümleri bir sıradan adres. Gibi bu yayını diyor ki:

Bunu çözmenin birçok yolu vardır [tekli ve çoklu miras ve sanal miras ile ilgili problemler]. Visual Studio derleyicisi bununla başa çıkmak için nasıl karar verir: Çok miras alınan bir sınıfın üye işlevine bir işaretçi gerçekten bir yapıdır. "Ve" Bir işlev işaretçisi döküm boyutunu değiştirebilir! "

Muhtemelen (in) güvenlik konusundaki benim tahminimden tahmin edebileceğiniz gibi, bir işaretçinin ham adres yerine bir yetenek gibi ele alındığı C / C ++ donanım / yazılım projelerinde yer aldım.

Devam edebilirdim, ama umarım bu fikri anlarsın.

KISA ÖZET (ki ben de en üste koyacağım):

(0) işaretçilerin adres olarak düşünülmesi genellikle iyi bir öğrenme aracıdır ve genellikle sıradan veri türlerine işaretçiler için gerçek bir uygulamadır.

(1) Ancak, çoğu, belki de çoğu, işlevlere yönelik işaretçiler adres değildir, ancak bir adresten daha büyüktür (genellikle 2X, bazen daha fazla) veya aslında işlev ve benzeri adresleri içeren bellekteki bir yapıya işaretçilerdir. sabit bir havuz.

(2) Veri üyelerine ve yöntemlere işaretçiler genellikle daha da yabancıdır.

(3) FAR ve YAKIN işaretçi sorunları olan eski x86 kodu

(4) Güvenli "yağ işaretçileri" olan birçok örnek, özellikle IBM AS / 400.

Eminim daha fazlasını bulabilirsin.


16 bit gerçek modda LinearAddress = SegmentRegister.Selector * 16 + Offset(not süreleri 16, 16'ya kaymaz). Korumalı modda LinearAddress = SegmentRegister.base + offset(herhangi bir çarpma yok; segment tabanı GDT / LDT'de saklanır ve segment kaydında olduğu gibi önbelleğe alınır ).
Alexey Frunze

Segment tabanı hakkında da haklısınız. Yanlış hatırlamıştım. İsteğe bağlı olarak 4K ile çarpılan segment sınırıdır. segment tabanının bir segment tanımlayıcısını bellekten bir segment kaydına yüklediğinde donanım tarafından şifrelenmesi gerekir.
Krazy Glew

4

İşaretçi, bir bellek konumunun adresini (genellikle başka bir değişkenin bellek adresini) tutmak için kullanılan başka bir değişkendir.


Yani, kuruş aslında bir hafıza adresi mi? Yazara katılmıyor musunuz? Sadece anlamaya çalışıyorum.
d0rmLife

İşaretçinin birincil işlevi bir şeye işaret etmektir. Bunun tam olarak nasıl gerçekleştiği ve gerçek bir adres olup olmadığı tanımlanmamıştır. Bir işaretçi gerçek bir adres değil, sadece bir kimlik / tanıtıcı olabilir.
Alexey Frunze

4

Bu şekilde görebilirsiniz. İşaretçi, adreslenebilir bellek alanındaki bir adresi temsil eden bir değerdir.


2
İşaretçinin gerçek bellek adresini içinde tutması gerekmez. Cevabımı ve altındaki yorumları görün.
Alexey Frunze

what .... yığındaki ilk değişkenin göstergesini 0 yazdırmaz. Nasıl uygulandığına bağlı olarak yığın çerçevesinin üstünü (veya altını) yazdırır.
thang

@thang İlk değişken için üst ve alt aynıdır. Ve bu durumda üst veya alt kısımların adresi nedir?
Valentin Radu

@ValentinRadu, neden denemiyorsun .. tabii ki denemedin.
thang

2
@thang Haklısın, bazı kötü varsayımlar yaptım, savunmamın burada 05:00 olduğunu.
Valentin Radu

3

İşaretçi, genellikle başka bir değişkenin bellek adresini içerebilen başka bir değişkendir. Bir değişken olan bir işaretçi de bir bellek adresine sahiptir.


1
Mutlaka bir adres değil. Btw, cevabınızı göndermeden önce mevcut cevapları ve yorumları okudunuz mu?
Alexey Frunze

3

AC işaretçisi bir bellek adresine çok benzer, ancak makineye bağlı ayrıntılar soyutlanmış ve ayrıca alt düzey komut kümesinde bulunmayan bazı özellikler vardır.

Örneğin, bir C işaretçisi nispeten zengin yazılmıştır. İşaretçiyi bir dizi yapı boyunca arttırırsanız, bir yapıdan diğerine güzel bir şekilde atlar.

İşaretçiler dönüştürme kurallarına tabidir ve derleme zamanı türü denetimi sağlar.

Kaynak kodu düzeyinde taşınabilir, ancak temsili farklılık gösterebilen özel bir "boş işaretçi" değeri vardır. İşaretçiye değeri sıfır olan bir tamsayı sabiti atarsanız, bu işaretçi boş işaretçi değerini alır. İşaretçiyi bu şekilde başlatırsanız aynı şekilde.

Bir işaretçi boole değişkeni olarak kullanılabilir: null dışındaysa true, null olduğunda false değerini test eder.

Bir makine dilinde, boş gösterici 0xFFFFFFFF gibi komik bir adresse, bu değer için açık testlere sahip olmanız gerekebilir. C bunu senden gizler. Boş gösterici 0xFFFFFFFF olsa bile, kullanarak test edebilirsiniz if (ptr != 0) { /* not null! */}.

Tip sistemini bozan işaretçilerin kullanımı tanımlanmamış davranışa neden olurken, makine dilinde benzer kod iyi tanımlanmış olabilir. Birleştiriciler yazdığınız talimatları bir araya getirecektir, ancak C derleyicileri yanlış bir şey yapmadığınız varsayımına göre optimize edecektir. Bir ederse float *pişaretçi bir işaret long ndeğişkeni ve *p = 0.0yürütülür, derleyici bu işlemek için gerekli değildir. Daha sonra kullanılması n, kayan değerin bit modelini okumak zorunda olmayacaktır, ancak belki de, ndokunulmamış olan "katı takma adlandırma" varsayımına dayanan optimize edilmiş bir erişim olacaktır ! Yani, programın iyi davrandığı ve pbuna işaret etmemesi gerektiği varsayımı n.

C'de kodlamaya işaretçiler ve verilere işaretçiler farklıdır, ancak birçok mimaride adresler aynıdır. Hedef mimarisi olmasa bile "yağ" işaretçileri olan C derleyicileri geliştirilebilir. Yağ işaretçileri, işaretçilerin yalnızca makine adresleri değil aynı zamanda sınır kontrolü için işaret edilen nesnenin boyutu hakkında bilgi gibi başka bilgiler de içerdiği anlamına gelir. Portatif olarak yazılan programlar bu tür derleyicilere kolayca bağlanacaktır.

Gördüğünüz gibi, makine adresleri ve C işaretçileri arasında birçok anlamsal fark var.


NULL işaretçileri tüm platformlarda düşündüğünüz gibi çalışmaz - lütfen yukarıdaki CiscoIPPhone'a verdiğim yanıta bakın. NULL == 0, yalnızca x86 tabanlı platformlarda bulunan bir varsayımdır. Konvansiyon, yeni platformların x86 ile eşleşmesi gerektiğini, ancak özellikle gömülü dünyada böyle olmadığını söylüyor. Düzenleme: Ayrıca, C donanımdan bir işaretçi yolun değerini soyut bir şey yapmaz - "ptr! = 0" NULL! = 0 olduğu bir platformda NULL testi olarak çalışmaz.
DX-MON

1
DX-MON, bu standart C için tamamen yanlıştır. NULL, 0 olarak ayrılmıştır ve ifadelerde birbirinin yerine kullanılabilir. Donanımdaki NULL işaretçi gösteriminin tüm 0 bit olup olmadığı, kaynak kodunda nasıl temsil edildiği ile ilgisizdir.
Mark Bessey

@ DX-MON Doğru gerçeklerle çalışmadığınızdan korkuyorum. C'de, bir integral sabit ifade, boş göstergenin boş adres olup olmadığına bakılmaksızın bir boş işaretçi sabiti olarak işlev görür. ptr != 0Boş bir test olmayan bir C derleyicisini biliyorsanız , lütfen kimliğini belirtin (ancak bunu yapmadan önce satıcıya bir hata raporu gönderin).
Kaz

Neye ulaştığınızı görüyorum, ancak boş işaretçiler hakkındaki yorumlarınız tutarsız çünkü işaretçileri ve bellek adreslerini karıştırıyorsunuz - tam olarak soruda belirtilen alıntıdan kaçınmayı öneriyor! Doğru ifade: C, sıfır ofsetindeki bir bellek adresinin yasal olup olmadığına bakılmaksızın, boş göstergeyi sıfır olarak tanımlar.
alexis

1
@alexis Bölüm ve ayet lütfen. C, boş göstericiyi sıfır olarak tanımlamaz. C, sıfır (veya değeri sıfır olan herhangi bir integral sabit ifadeyi) bir boş işaretçi sabitini belirtmek için bir sözdizimi olarak tanımlar . faqs.org/faqs/C-faq/faq (bölüm 5).
Kaz

3

İşaretçileri anlamadan önce nesneleri anlamamız gerekir. Nesneler, var olan ve adres adı verilen bir konum belirtecine sahip varlıklardır. İşaretçi, içeriği aşağıdaki işlemi destekleyen bir nesnenin adresi olarak yorumlanan Cbir türdeki diğer değişkenler gibi bir değişkendir pointer.

+ : A variable of type integer (usually called offset) can be added to yield a new pointer
- : A variable of type integer (usually called offset) can be subtracted to yield a new pointer
  : A variable of type pointer can be subtracted to yield an integer (usually called offset)
* : De-referencing. Retrieve the value of the variable (called address) and map to the object the address refers to.
++: It's just `+= 1`
--: It's just `-= 1`

İşaretçi, o anda başvurduğu nesnenin türüne göre sınıflandırılır. Önemli olan bilginin tek kısmı nesnenin boyutudur.

Herhangi bir nesne, nesnenin &konum belirleyicisini (adres) bir işaretçi nesne türü olarak alan bir işlemi (adresi) destekler. Bu, isimlendirici &türü, nesne türünün bir işaretçisi olan bir işaretçi yerine bir nesnenin işlemi olarak çağırmak mantıklı olacağından, isimlendirmeyi çevreleyen karışıklığı ortadan kaldırmalıdır .

Not Bu açıklama boyunca, bellek kavramını dışarıda bıraktım.


Genel bir sistemdeki genel bir işaretçinin soyut gerçekliği hakkındaki açıklamanızı seviyorum. Ancak, belki de hafızayı tartışmak yardımcı olacaktır. Aslında, kendim için konuşursak, biliyorum ...! Bağlantının tartışılması büyük resmi anlamak için çok yararlı olabilir. +1 neyse :)
d0rmLife

@ d0rmYaşam: Diğer cevaplarda daha büyük resmi kapsayan yeterli açıklama var. Sadece başka bir görüş olarak matematiksel soyut bir açıklama yapmak istedim. Ayrıca IMHO, &imlecin kendisinden ziyade bir Nesneye daha fazla bağlı olduğu için `` Adresi '' olarak adlandırırken daha az karışıklık yaratacaktır
Abhijit

Suç yok, ama kendime yeterli açıklamanın ne olduğuna karar vereceğim. Bir ders kitabı veri yapılarını ve bellek tahsisini tam olarak açıklamak için yeterli değildir. ;) .... neyse, Cevabınız hala yararlı , o roman olmasa bile.
d0rmLife

Bellek kavramı olmadan işaretçileri işlemek mantıklı değildir . Nesne hafızasız varsa, adresin olmadığı bir yerde olmalıdır - örneğin kayıtlarda. '&' Kullanabilmek için bellek gerektirir.
Aki Suihkonen

3

Adres, genellikle her bayt için bir tamsayı olarak bir sabit boyutlu depolama parçasını tanımlamak için kullanılır. Buna kesin olarak ISO C tarafından da kullanılan bayt adresi denir . Örneğin her bit için bir adres oluşturmak için başka yöntemler de olabilir. Ancak, yalnızca bayt adresi sıklıkla kullanılır, genellikle "bayt" ı atlarız.

Teknik olarak, bir adres hiçbir zaman C'de bir değer değildir, çünkü (ISO) C'deki "değer" teriminin tanımı:

Belirli bir türe sahip olarak yorumlandığında bir nesnenin içeriğinin kesin anlamı

(Benim tarafımdan vurgulanmıştır.) Bununla birlikte, C'de böyle bir "adres türü" yoktur.

İşaretçi aynı değil. Pointer bir tür tip C dilinde. Birkaç farklı işaretçi türü vardır. Dilin aynı kurallar dizisine uymalarına gerek yoktur, örneğin, ++türüne int*karşı bir değer üzerindeki etkisi char*.

C'deki bir değer işaretçi türünde olabilir. Buna işaretçi değeri denir . Açıkça belirtmek gerekirse, bir işaretçi değeri C dilinde bir işaretçi değildir. Ancak bunları bir araya getirmeye alışkınız, çünkü C'de belirsiz olması muhtemel değildir: bir ifadeyi p"işaretçi" olarak adlandırırsak, yalnızca bir işaretçi değeridir, ancak bir tür değildir, çünkü C'deki adlandırılmış bir tür değildir. bir ifade ifade fakat bir yan tip adı veya typedef-ad .

Diğer bazı şeyler ince. Bir C kullanıcısı olarak, öncelikle, ne objectanlama geldiğini bilmek gerekir :

yürütme ortamında, içeriği değerleri temsil edebilen veri depolama bölgesi

Nesne, belirli bir türdeki değerleri temsil eden bir varlıktır. İşaretçi bir nesne türüdür . Eğer beyan edersek int* p;, p"işaretçi türü bir nesne" veya "işaretçi nesnesi" anlamına gelir.

Standart tarafından normatif olarak tanımlanmış bir "değişken" olmadığını unutmayın (aslında normatif metinde ISO C tarafından bir isim olarak kullanılmaz). Bununla birlikte, gayri resmi olarak, bir nesneye başka bir dil gibi bir değişken diyoruz. (Ama yine de tam olarak değil, örneğin C ++ 'da bir değişken normalde referans tipi olabilir , ki bu bir nesne değildir.) "İşaretçi nesnesi" veya "işaretçi değişkeni" ifadeleri bazen yukarıdaki gibi "işaretçi değeri" gibi ele alınır. olası küçük bir fark. (Bir örnek daha "dizi" dir.)

İşaretçi bir tür olduğundan ve adres C'de etkin bir şekilde "türsüz" olduğundan, işaretçi değeri kabaca bir adresi "içerir". Ve işaretçi türü bir ifade için elde adres, örneğin

ISO C11 6.5.2.3

3 Tekli &operatör, işleneninin adresini verir.

Bu ifadenin WG14 / N1256, yani ISO C99: TC3 tarafından verildiğini unutmayın. C99'da var

3 Tekli &operatör, işleneninin adresini döndürür.

Komitenin görüşünü yansıtır: bir adres, tekli operatör tarafından döndürülen bir işaretçi değeri değildir& .

Yukarıdaki ifadelere rağmen, standartlarda bile biraz karışıklık var.

ISO C11 6.6

9 Bir adres sabiti boş bir işaretçi, statik depolama süresindeki bir nesneyi belirten bir lvalue işaretçisi veya bir işlev göstergesidir.

ISO C ++ 11 5.19

3 ... Adres sabit ifadesi , statik depolama süresine sahip bir nesnenin adresine, bir işlevin adresine veya boş bir işaretçi değerine veya bir ön değer çekirdek sabit ifadesine göre değerlendirilen işaretçi türünün bir ön çekirdek sabit ifadesidir. tür std::nullptr_t. ...

(Son C ++ standart taslağı başka bir ifade kullanır, bu nedenle bu sorun yoktur.)

Aslında hem C'deki "adres sabiti" hem de C ++'daki "adres sabiti ifadesi", işaretçi türlerinin (veya C ++ 11'den beri en azından "işaretçi benzeri" türlerin sabit ifadesidir).

Ve yerleşik tekli &işleç C ve C ++ 'da "adres" olarak adlandırılır; benzer şekilde, std::addressofC ++ 11 ile tanıtıldı.

Bu adlandırma yanlış anlama getirebilir. Yerine, / sonucu içeren bir adres verir: elde edilen ekspresyon bunlar olarak yorumlanabilir olur, böylece işaretçi tiptedir olan bir adres.


2

"Hangi adreslerin ne hakkında olduğunu bilmeyenlerin kafasını karıştırıyor" diyor - ayrıca, bu doğru: adreslerin ne hakkında olduğunu öğrenirseniz, kafanız karışmaz. Teorik olarak, işaretçi bir diğerini işaret eden, pratikte bir adresi tutan, işaret ettiği değişkenin adresi olan bir değişkendir. Bu gerçeği neden saklamam gerektiğini bilmiyorum , bu bir roket bilimi değil. İşaretçileri anlarsanız, bilgisayarların nasıl çalıştığını anlamak için bir adım daha yaklaşmış olursunuz. Devam et!


2

Düşünmeye gel, sanırım anlambilim meselesi. Yazarın doğru olduğunu düşünmüyorum, çünkü C standardı, burada daha önce belirtildiği gibi, referans verilen nesneye bir adres tutan bir işaretçiyi ifade ediyor. Ancak, adres! = Bellek adresi. Bir adres C standardına göre gerçekten herhangi bir şey olabilir, ancak sonunda bir bellek adresine yol açacaktır, işaretçinin kendisi bir kimlik, bir ofset + seçici (x86), herhangi bir belleği tanımlayabildiği sürece (eşledikten sonra) adreslenebilir alandaki adres.


Bir işaretçi bir adres tutar (veya boşsa, içermez). Ancak bu bir adres olmaktan çok uzaktır : örneğin, aynı adrese ancak farklı türde iki işaretçi birçok durumda eşdeğer değildir.
Gilles 'SO

Eğer görürseniz @Gilles olduğu gibi "olmak" int i=5> i - olduğu daha sonra 5, işaretçi adresi evet. Ayrıca, null'un da bir adresi vardır. Genellikle geçersiz bir yazma adresi (ancak zorunlu olarak, bkz. X86-real modu), ancak bir adres daha az değildir. Null için gerçekten sadece 2 gereksinim vardır: bir işaretçi ile eşitsiz olanı gerçek bir nesne ile karşılaştırmak garanti edilir ve herhangi iki boş işaretçi eşittir.
Valentin Radu

Aksine, bir boş göstergenin herhangi bir nesnenin adresine eşit olmadığı garanti edilir. Bir boş gösterici deşifre etmek tanımsız bir davranıştır. “İşaretçinin adres olduğunu” söylemenin büyük bir sorunu, onların farklı şekilde çalışmasıdır. Eğer pbir gösterici, p+1her zaman 1. göre artırılabilir adresi değildir
Gilles 'SO durdurma varlık şer'

Yorumu tekrar okuyun lütfen it's guaranteed to compare unequal to a pointer to an actual object. İşaretçi aritmetiği için, noktayı görmüyorum, "+" işlemi zorunlu olarak ona bir bayt eklemese bile, işaretçinin değeri hala bir adres.
Valentin Radu

1

Bir C veya C ++ işaretçisinin, diğer yanıtlarda görmediğim farklı işaretçi türleri nedeniyle basit bir bellek adresinden farklı olduğu başka bir yol (toplam boyutlarına rağmen, göz ardı etmiş olabilirim). Ancak muhtemelen en önemlisidir, çünkü deneyimli C / C ++ programcıları bile üzerinde gezinebilir:

Derleyici, uyumsuz tür işaretçilerin açıkça yapsalar bile aynı adrese işaret etmediğini varsayabilir; bu da basit bir pointer == adres modeliyle mümkün olmayacak davranışlar verebilir. Aşağıdaki kodu göz önünde bulundurun (varsayalım sizeof(int) = 2*sizeof(short)):

unsigned int i = 0;
unsigned short* p = (unsigned short*)&i;
p[0]=p[1]=1;

if (i == 2 + (unsigned short)(-1))
{
  // you'd expect this to execute, but it need not
}

if (i == 0)
{
  // you'd expect this not to execute, but it actually may do so
}

Bunun bir istisnası olduğuna dikkat edin char*, bu nedenle değerleri kullanarak değiştirmek char*mümkündür (çok taşınabilir olmasa da).


0

Hızlı özet: AC adresi, genellikle belirli bir türde makine düzeyinde bir bellek adresi olarak gösterilen bir değerdir.

Niteliksiz "pointer" sözcüğü belirsiz. C'nin işaretçi nesneleri (değişkenleri), işaretçi türleri , işaretçi ifadeleri ve işaretçi değerleri vardır .

"İmleç" kelimesini "imleç nesnesi" anlamında kullanmak çok yaygındır ve bu biraz karışıklığa neden olabilir - bu yüzden "imleci" bir isim olarak değil bir sıfat olarak kullanmaya çalışıyorum.

C standardı, en azından bazı durumlarda, "işaretçi değeri" anlamında "işaretçi" sözcüğünü kullanır. Örneğin, malloc'un tanımı "ayrılmış boşluğa bir boş gösterici ya da bir işaretçi döndürür" der.

Peki C'deki adres nedir? Bir işaretçi değeri, yani belirli bir işaretçi türünün değeri. (Boş gösterici değerinin mutlaka "adres" olarak anılması gerekmez, çünkü hiçbir şeyin adresi değildir).

Standardın tekli &operatörün tanımı, "operandının adresini verdiğini" söylüyor. C standardının dışında, "adres" kelimesi genellikle (fiziksel veya sanal) bir bellek adresine, genellikle bir kelime boyutuna (belirli bir sistemde "kelime" ne olursa olsun) atıfta bulunur.

AC "adresi" tipik olarak bir makine adresi olarak uygulanır - tıpkı bir C intdeğeri tipik olarak bir makine sözcüğü olarak uygulanır. Ancak bir C adresi (işaretçi değeri) bir makine adresinden daha fazlasıdır. Genellikle bir makine adresi olarak temsil edilen bir değerdir ve belirli bir tür ile bir değerdir .


0

İşaret değeri olan bir adres. İşaretçi değişkeni , bir adresi depolayabilen bir nesnedir. Bu doğrudur çünkü standardın işaretçi olarak tanımladığı şey budur. C acemilerine söylemek önemlidir, çünkü C acemileri genellikle bir işaretçi ve işaret ettiği şey arasındaki farktan belirsizdir (yani, bir zarf ile bir bina arasındaki farkı bilmiyorlar). Adres kavramı (her nesnenin bir adresi vardır ve bir işaretçinin sakladığı şey budur) önemlidir, çünkü bunu çözer.

Bununla birlikte, standart belirli bir soyutlama seviyesinde konuşuyor. Yazarın "hangi adreslerin hakkında olduğunu bilen", ancak C için yeni olanlardan bahsettiği insanlar mutlaka farklı bir soyutlama düzeyindeki adresleri - belki de montaj dilini programlayarak - öğrenmiş olmalıdırlar. C uygulamasının, CPU'ların opcod'larının kullandığı adresler için aynı temsili kullanmasının (bu pasajda "mağaza adresi" olarak anılır), bu kişilerin zaten bildiği gibi bir garanti yoktur.

"Mükemmel makul adres manipülasyonu" hakkında konuşmaya devam ediyor. C standardı ile ilgili olarak, temelde "mükemmel makul adres manipülasyonu" diye bir şey yoktur. Toplama işaretçiler üzerinde tanımlanır ve temelde budur. Elbette, bir işaretçiyi tamsayıya dönüştürebilir, biraz bitsel veya aritmetik işlem yapabilir ve sonra geri dönüştürebilirsiniz. Bunun standart tarafından çalışacağı garanti edilmez, bu nedenle bu kodu yazmadan önce C uygulamanızın işaretçileri nasıl temsil ettiğini ve bu dönüşümü nasıl gerçekleştirdiğini daha iyi bilirsiniz. Bu muhtemelen beklediğiniz adres gösterimi kullanır, ancak elle okumadım çünkü o en senin hatam değil. Bu bir karışıklık değil, yanlış programlama prosedürü ;-)

Kısacası, C, adresin yazardan daha soyut bir kavramını kullanır.

Yazarın elbette bir adres konsepti de konuyla ilgili en düşük seviye kelime değildir. Sanal bellek haritaları ve birden fazla yongada fiziksel RAM adresleme ile ne derseniz, CPU'ya erişmek istediğiniz "mağaza adresi" dir ve temel olarak istediğiniz verilerin donanımda bulunduğu yerle ilgisi yoktur. Her şey dolaylı ve temsil katmanları, ancak yazar ayrıcalık için birini seçti. Eğer C hakkında konuşurken bunu yapacaksanız, ayrıcalık için C seviyesini seçin !

Şahsen, C'nin montaj programcılarına tanıtılması bağlamında, yazarın sözlerinin bu kadar yararlı olduğunu düşünmüyorum. Üst düzey dillerden gelenlere, işaretçi değerlerinin adres olmadığını söylemek kesinlikle yararlı değildir. Karmaşıklığı kabul etmek, CPU'nun bir adresin ne olduğunu söyleme konusunda tekele sahip olduğunu ve bu nedenle C işaretçi değerlerinin "adresler" olmadığını söylemek daha iyidir. Bunlar adreslerdir, ama demek istedikleri adreslerden farklı bir dilde yazılabilirler. C bağlamında iki şeyi "adres" ve "mağaza adresi" olarak ayırmanın yeterli olacağını düşünüyorum.


0

Basitçe söylemek gerekirse, işaretçiler aslında segmentasyon mekanizmasının, segmentasyondan sonra Doğrusal Adres'e ve daha sonra çağrı yapıldıktan sonra Fiziksel adrese çevrilen bir kısmıdır. Fiziksel Adresler aslında sizin koçunuzdan adreslenir.

       Selector  +--------------+         +-----------+
      ---------->|              |         |           |
                 | Segmentation | ------->|  Paging   |
        Offset   |  Mechanism   |         | Mechanism |
      ---------->|              |         |           |
                 +--------------+         +-----------+
        Virtual                   Linear                Physical
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.