Dizi adı işaretçi mi?


203

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?


4
Hayır. Ancak dizi aynıdır ve dizi [0]

36
@pst: &array[0]bir dizi değil, bir işaretçi verir;)
jalf

28
@Nava (ve pst): dizi ve & dizi [0] aynı değildir. Burada örnek: sizeof (dizi) ve sizeof (& dizi [0]) farklı sonuçlar verir.
Thomas Padron-McCarthy

1
@Thomas kabul eder, ancak işaretçiler açısından, dizi ve & dizi [0] dereference olduğunda, aynı [0] dizisi değerini üretir .ie * dizi == dizi [0]. Kimse bu iki göstergenin aynı olduğu anlamına gelmiyordu, ancak bu özel durumda (ilk öğeye işaret ederek) dizinin adını da kullanabilirsiniz.
Nava Carmon

1
Bunlar da anlayışınıza yardımcı olabilir: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah

Yanıtlar:


255

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;

ptamsayılar için boşluk içermez, ancak bir tamsayı için bir boşluğa işaret edebilir. Örneğin, dizideki ailk 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 apointer içine p(anlamına gelir ne olursa olsun). Bunun yerine, dizi adı ailk öğesine bir işaretçiye dönüştürülür. Böylece bu ödev bir öncekiyle aynı şeyi yapar.

Artık pbir 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 yne 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ı aiç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, sizeofbir dizide işleci kullandığımızdır . Eğer abu bağlamda bir işaretçi dönüştürüldü, sizeof abu yüzden, bu durumda, bir ibrenin boyutu elde olup yerine gereksiz olur gerçek tadını arasında olacaktır adizi kendisi anlamına gelir.


5
Her iki - Benzer bir otomatik dönüştürme işlev işaretçileri uygulanır functionpointer()ve (*functionpointer)()gariptir, aynı anlama geliyor.
Carl Norum

3
Dizilerin ve işaretçilerin aynı olup olmadığını sormadı, ancak bir dizinin adı bir işaretçi ise
Ricardo Amores

32
Dizi adı işaretçi değildir. Öğe türünün işaretçisine örtük bir dönüşümü olan bir tür dizisi değişkeni için tanımlayıcıdır.
Pavel Minaev

29
Ayrıca, 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 intbir 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.
Pavel Minaev

3
@ onmyway133, kısa bir açıklama ve diğer alıntılar için burayı kontrol edin .
Carl Norum

37

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 */

21

Dizi türünün bir ifadesi (dizi adı gibi) daha büyük bir ifadede görünüyorsa ve ya &veya sizeofiş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 atü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, abir 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 sizeofveya 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 *aile 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 getsiş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.


  1. Hangi * adres değerinin yorumlanma şeklini etkileyebilir - makineye bağlıdır.

Bu cevap için oldukça uzun zamandır arıyoruz. Teşekkür ederim! Ve eğer bilirseniz, bir dizi ifadesinin ne olduğunu biraz daha anlatabilir misiniz? 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)?
Andriy Dmytruk

Ve bir şey daha. 5 uzunluklu bir dizi int [5] tipindedir. Yani sizeof'den (dizi) dediğimiz uzunluğu nereden biliyoruz? Ve bu, farklı uzunluktaki dizilerin farklı sabit türleri gibi olduğu anlamına mı geliyor?
Andriy Dmytruk

@AndriyDmytruk: sizeofbir 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, sizeoftek bir öğedeki bayt sayısıyla çarpılan öğelerin sayısını değerlendirir. A int4 bayt genişliğinde ise, 5 elemanlık bir dizi int20 bayt alır.
John Bode

Operatör de [ ]ö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 abir işaretçi türüne dönüştürülmez int*(ancak abir işlevin argümanı ise dönüştürülmelidir int*).
Stan

2
@Stan: İfade 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. ailk 3-eleman dizisine noktası int, a + 1ikinci 3-eleman dizisine noktaları int, *(a + 1) bir ikinci 3 öğeli bir dizi int, *(a + 1) + 2ikinci 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.
John Bode

5

Böyle bir dizi bildirildi

int a[10];

belleği 10 ints boyunca ayırır . Değiştiremezsiniz aancak 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 ints 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

2
Diziler her zaman yığına ayrılmaz. Yhat, derleyiciden derleyiciye değişecek bir uygulama detayıdır. Çoğu durumda, statik veya genel diziler yığından farklı bir bellek bölgesinden ayrılacaktır. Sabit türler dizileri belleğin başka bir bölgesinden ayrılabilir
Mark Bessey

1
Sanırım Grumdrig "10 intsaniye otomatik depolama süresi ayırıyor" demekti .
Yörüngedeki Hafiflik Yarışları

4

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]);

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 cve aaynı belleğe "nokta", adresini elde edebilirsiniz cpointer, ancak adresini alamıyor apointer.


1
"İyi derler (2 uyarı ile)". Bu iyi değil. Gcc'ye ekleyerek uygun standart C olarak derlenmesini söylerseniz -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.
Lundin

Lundin yorumunuz için teşekkür ederiz. Biliyorsunuz birçok standart var. Düzenlemede ne demek istediğimi açıklığa kavuşturmaya çalıştım. int **Tip bir daha kullanmalıdır orada nokta değil void *bunun için.
Palo

-3

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

-4

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'.


1
Dizi adı sabit işaretçiyle aynı değildir. Verilen: int a [10]; int * p = a; sizeof (p) ve sizeof (a) aynı değildir.
Mark Bessey

1
Başka farklılıklar da var. Genel olarak, C Standardı tarafından kullanılan ve özellikle "dönüşüm" olarak adlandırılan terminolojiye bağlı kalmak en iyisidir. Alıntı: "Sizeof operatörünün veya tekli ve operatörün işleneni veya bir diziyi başlatmak için kullanılan bir dize değişmezi dışında, '' tür dizisi '' türündeki bir ifade '' türündeki bir ifadeye dönüştürülür. dizi nesnesinin ilk öğesini işaret eden ve bir lvalue olmayan 'yazılacak işaretçi'. Dizi nesnesinin kayıt depolama sınıfı varsa, davranış tanımsızdır. "
Pavel Minaev

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.