Lütfen açıklamaya bir örnek ekleyin.
int *p;
bir tamsayıya bir işaretçi tanımlar ve *p
bu işaretçiyi kullanmaz, yani p'nin işaret ettiği veriyi alır.
Lütfen açıklamaya bir örnek ekleyin.
int *p;
bir tamsayıya bir işaretçi tanımlar ve *p
bu işaretçiyi kullanmaz, yani p'nin işaret ettiği veriyi alır.
Yanıtlar:
Bu var genellikle yeterince iyi - Eğer montaj programlama sürece - bir tahayyül etmek işaretçi 1 2, üçüncü 3 dördüncü ve böylece sürecin belleğinde ikinci bayt atıfta ile sayısal bellek adresini içeren ....
Eğer bellekte veri / değer erişmek istediğinizde işaretçi işaret ettiği - o sayısal indeksi adresin içeriğini - o zaman dereference işaretçi.
Farklı bilgisayar dillerinin derleyiciye veya yorumlayıcıya şimdi sivri uçlu nesnenin (geçerli) değeriyle ilgilendiğinizi belirtmek için farklı gösterimleri vardır - Aşağıda C ve C ++ üzerine odaklanıyorum.
p
Aşağıdaki gibi bir işaretçi verilen C'yi düşünün ...
const char* p = "abc";
... 'a', 'b', 'c' harflerini kodlamak için kullanılan sayısal değerlere sahip dört bayt ve metin verilerinin sonunu belirtmek için 0 bayt bellekte bir yerde saklanır ve bunun sayısal adresi veri depolanır p
. Bu şekilde C, bellekteki metni kodlar ASCIIZ olarak bilinir .
Örneğin, dize değişmez değeri 0x1000 adresinde ve p
32 bit işaretçi 0x2000'de olsaydı, bellek içeriği şöyle olur:
Memory Address (hex) Variable name Contents
1000 'a' == 97 (ASCII)
1001 'b' == 98
1002 'c' == 99
1003 0
...
2000-2003 p 1000 hex
Not orada adresinde 0x1000 için hiçbir değişken isim / tanımlayıcı, ama biz dolaylı adresini depolamak bir işaretçi kullanarak değişmez dize ifade ettiğini daha: p
.
Karakterlere atıfta bulunmak için, bu gösterimlerden birini (tekrar C için) kullanmaktan p
vazgeçtik p
:
assert(*p == 'a'); // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b'); // Another notation for p[1]
İşaretçileri sivri uçlu veriler arasında taşıyabilir ve gittikçe kayıtlarını kaldırabilirsiniz:
++p; // Increment p so it's now 0x1001
assert(*p == 'b'); // p == 0x1001 which is where the 'b' is...
Eğer yazılabilecek bazı verileriniz varsa, bunun gibi şeyler yapabilirsiniz:
int x = 2;
int* p_x = &x; // Put the address of the x variable into the pointer p_x
*p_x = 4; // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4
Yukarıda derleme zamanında bir değişkene ihtiyaç duyacağınızı x
bilmelisiniz ve kod, derleyiciden nerede saklanacağını ayarlamasını ve adresin kullanılabilir olmasını sağlamanızı ister &x
.
C'de, veri üyelerine sahip bir yapıya işaretçi olan bir değişkeniniz varsa, kayıt ->
silme işlecini kullanarak bu üyelere erişebilirsiniz :
typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159; // Dereference and access data member x.d_
(*p).d_ *= -1; // Another equivalent notation for accessing x.d_
Bir işaretçi kullanmak için, bir bilgisayar programının da işaret edilen veri türüne ilişkin bir kavrayışa ihtiyacı vardır - bu veri türünün temsil etmek için birden fazla bayta ihtiyacı varsa, işaretçi normalde verilerdeki en düşük numaralı bayta işaret eder.
Yani, biraz daha karmaşık bir örneğe bakmak:
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3); // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4); // Actually looks at bytes from address p + 1 * sizeof(double)
// (sizeof(double) is almost always eight bytes)
++p; // Advance p by sizeof(double)
assert(*p == 13.4); // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8; // Change sizes[3] from 19.4 to 29.8
// Note earlier ++p and + 2 here => sizes[3]
Bazen programınız çalışana kadar ne kadar belleğe ihtiyacınız olduğunu bilmiyorsunuz ve hangi verilerin ona atıldığını görüyorlar ... sonra kullanarak belleği dinamik olarak ayırabilirsiniz malloc
. Adresi bir işaretçide saklamak yaygın bir uygulamadır ...
int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10; // Dereference the pointer to the memory, then write a value in
fn(*p); // Call a function, passing it the value at address p
(*p) += 3; // Change the value, adding 3 to it
free(p); // Release the memory back to the heap allocation library
C ++ 'da, bellek ayırma normalde new
operatör ile yapılır ve aşağıdakilerle ayrılır delete
:
int* p = new int(10); // Memory for one int with initial value 10
delete p;
p = new int[10]; // Memory for ten ints with unspecified initial value
delete[] p;
p = new int[10](); // Memory for ten ints that are value initialised (to 0)
delete[] p;
Ayrıca aşağıdaki C ++ akıllı işaretçilerine de bakın.
Genellikle bir işaretçi, bazı verilerin veya ara belleğin bellekte nerede bulunduğunun tek göstergesi olabilir. Bu veri / arabellek sürekli olarak kullanılıyorsa free()
veya delete
hafızayı sızdırma veya aramadan kaçınma yeteneği gerekiyorsa, programlayıcının işaretçinin bir kopyası üzerinde çalışması gerekir ...
const char* p = asprintf("name: %s", name); // Common but non-Standard printf-on-heap
// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
if (!isprint(*q))
*q = '_';
printf("%s\n", p); // Only q was modified
free(p);
... veya herhangi bir değişikliğin geri alınmasını dikkatli bir şekilde düzenleyin ...
const size_t n = ...;
p += n;
...
p -= n; // Restore earlier value...
free(p);
C ++ 'da, işaretçileri saklamak ve yönetmek için akıllı işaretçi nesnelerini kullanmak , akıllı işaretçilerin yıkıcıları çalıştığında bunları otomatik olarak yeniden konumlandırmak en iyi uygulamadır . C ++ 11'den beri Standart Kütüphane iki tane sunar, unique_ptr
çünkü ayrılan bir nesne için tek bir sahip olduğunda ...
{
std::unique_ptr<T> p{new T(42, "meaning")};
call_a_function(p);
// The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete
... ve shared_ptr
hisse sahipliği için ( referans sayımı kullanarak ) ...
{
auto p = std::make_shared<T>(3.14, "pi");
number_storage1.may_add(p); // Might copy p into its container
number_storage2.may_add(p); // Might copy p into its container } // p's destructor will only delete the T if neither may_add copied it
C'de NULL
ve 0
- ve ek olarak C ++ ' nullptr
da, bir işaretçinin bir değişkenin bellek adresini şu anda tutmadığını ve işaretleyicinin aritmetik işleminde kullanılmaması veya kullanılmaması gerektiğini belirtmek için kullanılabilir. Örneğin:
const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
switch (c) {
case f: p_filename = optarg; break;
}
if (p_filename) // Only NULL converts to false
... // Only get here if -f flag specified
C ve C ++, dahili sayısal türleri mutlaka varsayılan yok gibi 0
, ne bools
kadar false
, işaretçileri her zaman ayarlı olmayan NULL
. Tüm bunlar static
değişken olduğunda veya statik nesnelerin veya tabanlarının doğrudan veya dolaylı üye değişkenleri olduğunda veya sıfır başlatma işlemine (örn. new T();
Ve new T(x, y, z);
işaretçiler dahil T üyelerinde sıfır başlatma gerçekleştirirken ) 0 / false / NULL olarak ayarlanır. new T;
değil).
Ayrıca, ve bir işaretçiye atadığınızda 0
, işaretçideki bitlerin tümü sıfırlanmayabilir: işaretçi donanım düzeyinde "0" içermeyebilir veya sanal adres alanınızdaki 0 adresine başvurabilir. Derleyici başka orada hiç bir neden varsa mağaza şeye izin ama ne yaparsa edilir - sen geldin ve işaretçiyi karşılaştırırsanız , , ya da bunlardan biri atandı başka işaretçi, karşılaştırma zorunluluk çalışması beklendiği gibi. Yani, derleyici düzeyindeki kaynak kodu altında, "NULL" C ve C ++ dillerinde potansiyel olarak biraz "büyülü" ...NULL
nullptr
0
NULL
nullptr
Daha kesin olarak, başlatılan işaretçiler ya NULL
( ya da genellikle sanal ) bir bellek adresini tanımlayan bir bit kalıbı saklar .
Basit durum, bunun sürecin tüm sanal adres alanına sayısal bir ofset olduğu yerdir; daha karmaşık durumlarda işaretçi, CPU'nun CPU "segment" kayıtlarına veya bit örüntüsünde kodlanan bir segment kimliği türüne göre seçebileceği ve / veya adres kullanarak makine kodu talimatları.
Örneğin, bir değişkene int*
işaret etmek için uygun şekilde başlatılan - "GPU" belleğindeki int
bir float*
erişim belleğine değiştikten int
sonra , değişkenin bulunduğu bellekten oldukça farklı olabilir , daha sonra bir kez bir işlev işaretçisi olarak yayınlanır ve bir işlev işaretçisi olarak kullanılırsa program için ayrı bellek tutma makinesi opcodes ( int*
bu diğer bellek bölgelerinde etkili bir şekilde rastgele, geçersiz bir işaretçinin sayısal değeri ile ).
C ve C ++ gibi 3GL programlama dilleri bu karmaşıklığı gizleme eğilimindedir, böylece:
Derleyici bir değişkene veya işleve bir işaretçi verirse, onu serbestçe serbest bırakabilirsiniz (değişkenin zarar görmediği / dağıtılmadığı sürece) ve derleyicinin sorunu, örneğin belirli bir CPU segmenti kaydının önceden geri yüklenmesi gerekip gerekmediği veya kullanılan farklı makine kodu talimatı
Dizideki bir öğeye işaretçi alırsanız, dizideki başka herhangi bir yere gitmek için işaretçi aritmetiği kullanabilir veya hatta dizinin öğelere diğer işaretçilerle karşılaştırılması yasal olan bir adresin sonunu geçecek bir adres oluşturabilirsiniz. dizide (veya benzer şekilde işaretçi aritmetiği tarafından aynı son-son değerine taşınmış); yine C ve C ++ 'da, bu "sadece çalışır" sağlamak derleyiciye kalmış
Paylaşılan bellek eşlemesi gibi belirli işletim sistemi işlevleri size işaretçiler verebilir ve onlar için anlamlı olan adres aralığında "çalışır"
Yasal işaretçileri bu sınırların ötesine taşıma veya işaretleyicilere rastgele sayılar kullanma veya ilişkisiz türlere atanmış işaretçiler kullanma girişimleri, genellikle tanımlanmamış davranışa sahiptir , bu nedenle daha üst düzey kitaplıklarda ve uygulamalarda kaçınılmalıdır, ancak işletim sistemleri, aygıt sürücüleri vb. C veya C ++ Standardı tarafından tanımlanmamış bırakılmış davranışlara dayanması gerekebilir, bu yine de özel uygulamaları veya donanımları tarafından iyi tanımlanmıştır.
p[1]
ve *(p + 1)
aynı ? Yani, aynı talimatları yapar p[1]
ve *(p + 1)
üretir mi?
p
sadece 2000'dir: başka bir işaretçiniz p
varsa, 2000'i dört veya sekiz baytında saklamanız gerekir. Umarım yardımcı olur! Şerefe.
u
bir dizi içeriyorsa arr
, hem gcc hem de clang, değerin u.arr[i]
diğer sendika üyeleriyle aynı depolama alanına erişebileceğini tanır, ancak değerin *(u.arr+i)
bunu yapabileceğini fark etmez. Bu derleyicilerin yazarlarının, ikincisinin UB'yi çağırdığını mı, yoksa eski UB'yi mi çağırdığını düşünüyor musunuz, ancak yine de yararlı bir şekilde işlemek zorundalar, ancak iki ifadeyi açıkça farklı görüyorlar.
İşaretçinin kaydının kaldırılması, işaretçinin işaret ettiği bellek konumunda depolanan değerin alınması anlamına gelir. Operatör * bunu yapmak için kullanılır ve kayıt silme operatörü olarak adlandırılır.
int a = 10;
int* ptr = &a;
printf("%d", *ptr); // With *ptr I'm dereferencing the pointer.
// Which means, I am asking the value pointed at by the pointer.
// ptr is pointing to the location in memory of the variable a.
// In a's location, we have 10. So, dereferencing gives this value.
// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
*ptr = 20; // Now a's content is no longer 10, and has been modified to 20.
[]
de bir işaretçi ( a[b]
anlam olarak tanımlanmıştır *(a + b)
) dereferences olduğunu ekleyelim .
İşaretçi bir değere "referans" tır. Kütüphane çağrı numarası gibi bir kitap referansıdır. "Dereferencing" çağrı numarası fiziksel olarak bu kitabı alıyor ve alıyor.
int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;
// The * causes pA to DEREFERENCE... `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4..
Kitap orada değilse, kütüphaneci bağırmaya başlar, kütüphaneyi kapatır ve birkaç kişi orada olmayan bir kitap bulacak kişinin nedenini araştırmaya hazırdır.
Basit bir ifadeyle, kayıt silme, değerin, işaretçinin işaret ettiği belirli bir hafıza konumundan erişilmesi anlamına gelir.
İşaretçi Temelleri'nden kod ve açıklama :
Dereference işlemi işaretçiden başlar ve sivrisine erişmek için okunu takip eder. Amaç, sivri devlete bakmak veya sivri devleti değiştirmek olabilir. İşaretçideki dereference işlemi yalnızca işaretçide bir nokta işareti varsa çalışır - nokta işareti tahsis edilmeli ve işaretçi işaret edecek şekilde ayarlanmalıdır. İşaretçi kodundaki en yaygın hata, pointee'yi ayarlamayı unutmaktır. Koddaki bu hata nedeniyle en yaygın çalışma zamanı çökmesi başarısız bir dereference işlemidir. Java'da yanlış dereference çalışma zamanı sistemi tarafından kibarca işaretlenecektir. C, C ++ ve Pascal gibi derlenmiş dillerde, yanlış dereference bazen çökebilir ve diğer zamanlarda hafızayı bazı ince, rastgele bir şekilde bozar.
void main() {
int* x; // Allocate the pointer x
x = malloc(sizeof(int)); // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
}
Önceki tüm cevapların yanlış olduğunu düşünüyorum, çünkü kayıt silme gerçek değere erişmek anlamına geliyor. Wikipedia bunun yerine doğru tanımı verir: https://en.wikipedia.org/wiki/Dereference_operator
Bir işaretçi değişkeni üzerinde çalışır ve işaretçi adresindeki değere eşdeğer bir l değeri döndürür. Buna işaretçiyi "kayıttan çıkarma" denir.
Bununla birlikte, işaret ettiği değere hiç erişmeden işaretçiyi serbest bırakabiliriz. Örneğin:
char *p = NULL;
*p;
NULL işaretçisine değerine erişmeden başvurusunu iptal ettik. Ya da yapabiliriz:
p1 = &(*p);
sz = sizeof(*p);
Yine, kayıttan çıkarma, ancak değere asla erişme. Böyle bir kod çökmez: Verilere gerçekte geçersiz bir işaretçi ile eriştiğinizde çökme meydana gelir . Bununla birlikte, ne yazık ki, standarda göre, gerçek verilere dokunmaya çalışmasanız bile, geçersiz bir işaretçinin silinmesi tanımlanmamış bir davranıştır (birkaç istisna dışında).
Kısacası: işaretçinin kayıttan çıkarılması, kayıttan çıkarma operatörünün ona uygulanması anlamına gelir. Bu operatör, ileride kullanmanız için bir l değeri döndürür.
*p;
tanımlanmamış davranışa neden olur. Eğer değer erişmez dereferencing doğru olduğunu olmasına rağmen , kendi başına , kod *p;
yapar erişim değerini.