Bir dizinin adı C'de bir işaretçi mi? Değilse, bir dizinin adı ile işaretçi değişkeni arasındaki fark nedir?
&array[0]
bir dizi değil, bir işaretçi verir;)
Bir dizinin adı C'de bir işaretçi mi? Değilse, bir dizinin adı ile işaretçi değişkeni arasındaki fark nedir?
&array[0]
bir dizi değil, bir işaretçi verir;)
Yanıtlar:
Dizi bir dizi ve işaretçi bir işaretçidir, ancak çoğu durumda dizi adları işaretçilere dönüştürülür . Sık kullanılan bir terim onlar olmasıdır çürümesine işaretçilere.
İşte bir dizi:
int a[7];
a
yedi tamsayı için alan içerir ve bunlardan birine ödevle bir değer koyabilirsiniz, şöyle:
a[3] = 9;
İşte bir işaretçi:
int *p;
p
tamsayılar için boşluk içermez, ancak bir tamsayı için bir boşluğa işaret edebilir. Örneğin, dizideki a
ilk yer gibi yerlerden birine işaret edecek şekilde ayarlayabiliriz :
p = &a[0];
Kafa karıştırıcı olabilecek şey, bunu da yazabilmenizdir:
p = a;
Bu mu değil dizinin içeriğini kopyalamak a
pointer içine p
(anlamına gelir ne olursa olsun). Bunun yerine, dizi adı a
ilk öğesine bir işaretçiye dönüştürülür. Böylece bu ödev bir öncekiyle aynı şeyi yapar.
Artık p
bir diziye benzer şekilde kullanabilirsiniz:
p[3] = 17;
Bunun çalışmasının nedeni, C, dizisindeki dereferencing operatörünün [ ]
işaretçiler olarak tanımlanmış olmasıdır. x[y]
araçlar: pointer ile başlar x
, adım y
ne işaretçi puandan sonra ileri unsurları ve sonra ne varsa alır. İşaretçi aritmetik sözdizimi kullanılarak, x[y]
olarak da yazılabilir *(x+y)
.
Bu, sonra rezerve normal dizi ile çalışması için a
, adı a
içinde a[3]
şıra (birinci elemanın bir işaretçi dönüştürülebilir a
). Sonra 3 öğeyi ileri doğru atıyoruz ve orada ne varsa alıyoruz. Başka bir deyişle: öğeyi dizideki 3. konumda alın. (Birincisi 0 olarak numaralandırıldığı için dizideki dördüncü öğedir.)
Özetle, bir C programındaki dizi adları (çoğu durumda) işaretçilere dönüştürülür. Tek istisna, sizeof
bir dizide işleci kullandığımızdır . Eğer a
bu bağlamda bir işaretçi dönüştürüldü, sizeof a
bu yüzden, bu durumda, bir ibrenin boyutu elde olup yerine gereksiz olur gerçek tadını arasında olacaktır a
dizi kendisi anlamına gelir.
functionpointer()
ve (*functionpointer)()
gariptir, aynı anlama geliyor.
sizeof()
hiçbir dizi-> işaretçi çürümesi olmayan diğer bağlam operatördür &
- yukarıdaki örnekte, tek bir işaretçiye değil &a
, 7 dizisine int
bir işaretçi olacaktır int
; yani, türü int(*)[7]
dolaylı olarak dönüştürülemeyen tür olacaktır int*
. Bu şekilde, işlevler aslında belirli boyuttaki dizilere işaret edebilir ve tür sistemi aracılığıyla kısıtlamayı uygulayabilir.
Bir dizi değer olarak kullanıldığında, adı ilk öğenin adresini temsil eder.
Bir dizi değer olarak kullanılmadığında, adı tüm diziyi temsil eder.
int arr[7];
/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */
/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
Dizi türünün bir ifadesi (dizi adı gibi) daha büyük bir ifadede görünüyorsa ve ya &
veya sizeof
işleçlerinin işleneni değilse , dizi ifadesinin türü "T öğesinin N öğesi dizisinden" "işaretçi T" ve ifadenin değeri dizideki ilk öğenin adresidir.
Kısacası, dizi adı bir işaretçi değildir, ancak çoğu bağlamda bir işaretçi gibi ele alınır .
Düzenle
Yorumdaki soruyu cevaplama:
Eğer sizeof kullanırsam, sadece dizinin elemanlarının boyutunu sayar mıyım? O zaman “head” dizisi, uzunluk ve bir işaretçi hakkındaki bilgilerle de yer kaplar (ve bu, normal bir işaretçiden daha fazla yer kapladığı anlamına gelir)?
Bir dizi oluşturduğunuzda, ayrılan tek alan öğelerin kendileri için olan alandır; ayrı bir işaretçi veya meta veriler için depolama yapılmaz. verilmiş
char a[10];
hafızada ne alırsın
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
İfade a
, tüm dizi anlamına gelir, ancak orada nesne a
dizi elemanlarının kendileri ayrı. Böylece, sizeof a
tüm dizinin boyutunu (bayt cinsinden) verir. İfade , ilk öğenin adresiyle aynı olan&a
dizinin adresini verir . Arasındaki fark ve sonuç türüdür 1 - birinci durumda ikinci. &a
&a[0]
char (*)[10]
char *
İşlerin garipleştiği yer, ayrı ayrı öğelere erişmek istediğinizde - ifadenin a[i]
sonucu olarak tanımlanır *(a + i)
- bir adres değeri verildiğinde a
, i
öğeleri o adresten ofset ( bayt değil ) ofset ve sonucu dereference.
Sorun, a
bir işaretçi veya adres değil - tüm dizi nesnesidir. Bu nedenle, C'deki kural, derleyici dizi türünde bir ifade gördüğünde (örneğin a
, türü olan char [10]
) ve bu ifadenin sizeof
veya tekli &
işleçlerin işleneni olmadığında, bu ifadenin türü dönüştürülür ("bozulmalar") bir işaretçi türüne ( char *
) gidin ve ifadenin değeri dizinin ilk öğesinin adresidir. Bu nedenle, ifade a
, ifade ile aynı tür ve değere sahiptir &a[0]
(ve uzantı, ifade *a
ile aynı tür ve değere sahiptir a[0]
).
C B olarak adlandırılan önceki bir dilden türemiş ve B'de edildi a
oldu dizi öğelerinin ayrı bir işaretçi nesne a[0]
, a[1]
vb Ritchie B'nin dizi semantiğini tutmak istedim, ama o ayrı işaretçi nesne depolama uğraşmak istemiyordu. Böylece ondan kurtuldu. Bunun yerine derleyici, çeviri sırasında gerektiği gibi dizi ifadelerini işaretçi ifadelerine dönüştürür.
Dizilerin boyutlarıyla ilgili herhangi bir meta veri saklamadığını söylediğimi unutmayın. Bu dizi ifadesi bir işaretçiye "bozulur", sahip olduğunuz tek şey tek bir öğenin işaretçisidir. Bu öğe, bir dizi öğenin ilki olabilir veya tek bir nesne olabilir. İşaretçinin kendisine göre bilmenin bir yolu yoktur.
Bir işleve dizi ifadesi ilettiğinizde, tüm işlev ilk öğeye bir işaretçi olur - dizinin ne kadar büyük olduğu hakkında hiçbir fikri yoktur (bu nedenle gets
işlev böyle bir tehdittir ve sonunda kitaplıktan kaldırılmıştır). İşlevin, dizinin kaç öğeye sahip olduğunu bilmesi için, bir sentinel değeri (C dizelerindeki 0 sonlandırıcı gibi) kullanmanız veya öğe sayısını ayrı bir parametre olarak iletmeniz gerekir.
sizeof
bir işleçtir ve işlenendeki sayı baytlarını (bir nesneyi gösteren bir ifade veya parantez içinde bir tür adı) değerlendirir. Bu nedenle, bir dizi için, sizeof
tek bir öğedeki bayt sayısıyla çarpılan öğelerin sayısını değerlendirir. A int
4 bayt genişliğinde ise, 5 elemanlık bir dizi int
20 bayt alır.
[ ]
özel değil mi? Örneğin int a[2][3];
, o zaman x = a[1][2];
, yeniden yazılabilir olsa da x = *( *(a+1) + 2 );
, burada a
bir işaretçi türüne dönüştürülmez int*
(ancak a
bir işlevin argümanı ise dönüştürülmelidir int*
).
a
, yazmak için int [2][3]
" çürüyen " türe sahiptir int (*)[3]
. İfadenin *(a + 1)
türü int [3]
"bozulur" int *
. Böylece *(*(a + 1) + 2)
tip olacak int
. a
ilk 3-eleman dizisine noktası int
, a + 1
ikinci 3-eleman dizisine noktaları int
, *(a + 1)
bir ikinci 3 öğeli bir dizi int
, *(a + 1) + 2
ikinci dizinin, üçüncü elemanına noktaları int
, yani *(*(a + 1) + 2)
bir ikinci dizi üçüncü unsuru int
. Bunun makine koduyla nasıl eşleştirildiği tamamen derleyiciye kalmıştır.
Böyle bir dizi bildirildi
int a[10];
belleği 10 int
s boyunca ayırır . Değiştiremezsiniz a
ancak ile işaretçi aritmetiği yapabilirsiniz a
.
Bunun gibi bir işaretçi yalnızca işaretçi için bellek ayırır p
:
int *p;
Herhangi bir int
s ayırmaz . Bunu değiştirebilirsiniz:
p = a;
ve dizi aboneliklerini aşağıdaki gibi olabildiğince kullanın:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
saniye otomatik depolama süresi ayırıyor" demekti .
Dizi adı tek başına bir bellek konumu verir, böylece dizi adına bir işaretçi gibi davranabilirsiniz:
int a[7];
a[0] = 1976;
a[1] = 1984;
printf("memory location of a: %p", a);
printf("value at memory location %p is %d", a, *a);
İşaretçiye yapabileceğiniz diğer şık şeyler (örneğin, bir ofset ekleme / çıkarma), bir diziye de yapabilirsiniz:
printf("value at memory location %p is %d", a + 1, *(a + 1));
Dil açısından, C diziyi sadece bir çeşit "işaretçi" olarak göstermediyse (bilgiçliksel olarak sadece bir bellek konumudur. Bellekte keyfi bir konuma işaret edemez ve programcı tarafından kontrol edilemez). Bunu her zaman kodlamamız gerekir:
printf("value at memory location %p is %d", &a[1], a[1]);
Bence bu örnek konuya ışık tutuyor:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
Gcc 4.9.2'de iyi (2 uyarı ile) derler ve aşağıdakileri yazdırır:
a == &a: 1
ayy :-)
Böylece, sonuç hayırdır, dizi bir işaretçi değildir, ve işleci ile adresini elde edebileceğiniz için, bir işaretçi olarak, bir işaretçi olarak bellekte (salt okunur değil) depolanmaz. . Ama - oops - bu operatör çalışmıyor :-)), her iki durumda da, uyarıldınız:
p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
int **b = &a;
^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
printf("a == &a: %d\n", a == b);
C ++, derleme zamanında hataları ile bu tür girişimleri reddeder.
Düzenle:
Ben göstermek istedim budur:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
void *c = a;
void *b = &a;
void *d = &c;
printf("a == &a: %d\n", a == b);
printf("c == &c: %d\n", c == d);
return 0;
}
Olsa c
ve a
aynı belleğe "nokta", adresini elde edebilirsiniz c
pointer, ancak adresini alamıyor a
pointer.
-std=c11 -pedantic-errors
, geçersiz C kodu yazmak için bir derleyici hatası alırsınız. Bunun nedeni , birbiriyle hiçbir ilgisi olmayan iki tür olan int (*)[3]
bir değişkene a atamaya çalışmanızdır int**
. Öyleyse bu örneğin kanıtlaması gereken şey, hiçbir fikrim yok.
int **
Tip bir daha kullanmalıdır orada nokta değil void *
bunun için.
Dizi adı bir işaretçi gibi davranır ve dizinin ilk öğesini işaret eder. Misal:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
Her iki yazdırma ifadesi de bir makine için aynı çıktıyı verecektir. Sistemimde verdi:
0x7fff6fe40bc0
Dizi, bellekteki güvenli ve bitişik öğelerin bir koleksiyonudur. C'de bir dizinin adı ilk öğenin dizinidir ve bir uzaklık uygulayarak öğelerin geri kalanına erişebilirsiniz. Bir "ilk öğeye indeks" aslında hafıza yönüne bir göstergedir.
İşaretçi değişkenleri ile fark, dizinin adının işaret ettiği konumu değiştirememenizdir, dolayısıyla sabit bir işaretçiye benzer (benzerdir, aynı değildir. Mark'ın yorumuna bakın). Ancak işaretçi aritmetiği kullanırsanız değeri elde etmek için dizi adını kullanmanız gerekmez:
char array = "hello wordl";
char* ptr = array;
char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'
Yani cevap biraz 'evet'.
Dizi adı, bir dizinin 1. öğesinin adresidir. Yani evet dizi adı sabit bir işaretçi.