Neden bir dizinin adresi C'deki değerine eşittir?


189

Aşağıdaki kod bitinde, işaretçi değerleri ve işaretçi adresleri beklendiği gibi farklılık gösterir.

Ancak dizi değerleri ve adresleri yok!

Bu nasıl olabilir?

Çıktı

my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>

int main()
{
  char my_array[100] = "some cool string";
  printf("my_array = %p\n", my_array);
  printf("&my_array = %p\n", &my_array);

  char *pointer_to_array = my_array;
  printf("pointer_to_array = %p\n", pointer_to_array);
  printf("&pointer_to_array = %p\n", &pointer_to_array);

  printf("Press ENTER to continue...\n");
  getchar();
  return 0;
}

Comp.lang.c SSS'den: - [Yani C'deki `` işaretçi ve dizilerin denkliği '' ne anlama geliyor? ] ( c-faq.com/aryptr/aryptrequiv.html ) - [Dizi referansları işaretçilere bozulduğundan , arr bir dizi ise arr ve & arr arasındaki fark nedir? ] ( c-faq.com/aryptr/aryvsadr.html ) Veya tüm Diziler ve İşaretçiler bölümünü okuyun.
09:18

3
Bu soruya iki yıl önce bu diyagrama bir cevap ekledim Ne sizeof(&array)geri dönüyor?
Grijesh Chauhan

Yanıtlar:


214

Bir dizinin adı, dizi birinci elemanının adresine değerlendirilir, bu nedenle arrayve &arrayaynı değere sahip (fakat farklı tipte çok array+1ve &array+1olacak olmayan dizi 1'den fazla eleman uzunsa eşit).

Bunun iki istisnası vardır: dizi adı bir işlenen sizeofveya tekli &(adres-değeri) olduğunda, ad dizi nesnesinin kendisine karşılık gelir. Böylece sizeof array, işaretçinin boyutunu değil, tüm dizinin bayt cinsinden boyutunu verir.

Olarak tanımlanan bir dizi T array[size]için türe sahip olacaktır T *. Artırdığınızda / artırdığınızda, dizideki bir sonraki öğeye erişirsiniz.

&arrayaynı adresi değerlendirir, ancak aynı tanım göz önüne alındığında, bu tür bir işaretçi oluşturur T(*)[size]- yani, tek bir öğeye değil, bir diziye işaret eder. Bu işaretçiyi artırırsanız, tek bir öğenin boyutunu değil tüm dizinin boyutunu ekler. Örneğin, böyle bir kodla:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

İkinci işaretçinin birinciden 16 daha büyük olmasını bekleyebiliriz (çünkü 16 karakterlik bir dizi). % P tipik olarak işaretçileri onaltılık biçime dönüştürdüğünden, şuna benzeyebilir:

0x12341000    0x12341010

3
@Alexandre: &arraydizinin ilk öğesine arrayişaret eder , burada tam diziye karşılık gelir. Temel fark karşılaştırılarak görülebilir sizeof(array)için sizeof(&array). Ancak, arraybir işleve bağımsız değişken olarak geçerseniz , yalnızca &arraygerçekte iletildiğini unutmayın. Bir diziyi a ile kapsüllenmedikçe değere göre geçiremezsiniz struct.
Clifford

14
@Clifford: Diziyi bir işleve geçirirseniz, diziye bir işaretçi olmayacak şekilde &array[0], etkin bir şekilde geçirildiği ilk öğesinin bir işaretçisine bozulur &array. Bir nit-pick olabilir ama netleştirmek önemlidir; . işlevi geçirilen pointer türüne uygun bir prototip varsa derleyiciler uyaracaktır
CB Bailey

2
@ Jerry Coffin Örneğin int * p = & a, int göstergesinin p adres adresini istiyorsam, & p yapabilirim. & Array, tüm dizinin adresine (ilk öğenin adresinde başlayan) dönüştürür. Öyleyse dizi işaretçisinin (dizideki ilk öğenin adresini depolayan) bellek adresini nasıl bulabilirim? Hafızanın bir yerinde olmalı değil mi?
John Lee

2
@JohnLee: Hayır, dizinin herhangi bir yerinde diziye bir işaretçi olması gerekmez. Eğer bir işaretçi oluşturursanız, o zaman onun adresini alabilir: int *p = array; int **pp = &p;.
Jerry Coffin

3
@Clifford ilk yorum yanlış, neden hala saklıyor? Aşağıdaki (@Charles) cevabını okumayanlar için yanlış anlaşılmaya yol açabileceğini düşünüyorum.
Rick

30

Bunun nedeni, dizi adının ( my_array) işaretçiden diziye farklı olmasıdır. Bir dizinin adresinin takma adıdır ve adresi dizinin kendisinin adresi olarak tanımlanır.

Ancak işaretçi yığındaki normal bir C değişkenidir. Böylece adresini alabilir ve içerdiği adresten farklı bir değer elde edebilirsiniz.

Bu konu hakkında burada yazdım - lütfen bir göz atın.


& My_array, my_array'ın değeri yığın üzerinde olmadığından geçersiz bir işlem olmamalı, yalnızca my_array [0 ... length] var mı? Sonra her şey mantıklı olurdu ...
Alexandre

@Alexandre: Aslında neden izin verildiğinden emin değilim.
Eli Bendersky

registerDepolama süresi ne olursa olsun herhangi bir değişkenin (işaretlenmemişse ) adresini alabilirsiniz : statik, dinamik veya otomatik.
CB Bailey

my_arrayçünkü kendisi, yığın üzerinde my_array olan tüm dizi.
caf

3
my_array, &veya sizeofişleçlerinin öznesi olmadığında, ilk öğesinin (yani &my_array[0]) bir işaretçisine değerlendirilir - ancak my_arraykendisi bu işaretçi değildir ( my_arrayhala dizidir). Bu işaretçi sadece geçici bir değerdir (örn. Verilen int a;, aynen öyle a + 1) - kavramsal olarak en azından "gerektiği gibi hesaplanır". Gerçek "değeri" my_array, tüm dizinin içeriğidir - sadece bu değeri C'ye sabitlemek, bir kavanoza sis yakalamaya çalışmak gibidir.
caf

28

Bir ifadede, bir dizinin adı kullanmak zaman adres ve (işlenen olmadıkça C, (bir işleve geçen dahil) &) operatör veya sizeofoperatör, bu azalır ilk elemana için bir işaretçi.

Yani, çoğu bağlamda hem tür hem de değer arrayile eşdeğerdir &array[0].

Örnekte, my_arraytürüne sahip char[100]bir zayıflar char*Eğer printf bunu geçerken.

&my_arraytürü vardır char (*)[100](100'den diziye işaretçi char). İşlenen olduğu için &, bu, my_arrayilk öğesinin bir işaretçisine hemen çürümeyen durumlardan biridir .

Dizinin işaretçisi, bir dizi nesnesi olarak dizinin ilk öğesine bir işaretçi ile aynı adres değerine sahiptir, ancak bir dizinin işaretçisi öğenin işaretçisi için farklı bir türe sahiptir. o dizi. İki işaretçi türünde işaretçi aritmetiği yaptığınızda bu önemlidir.

pointer_to_arraytype char *- dizinin ilk öğesini işaret my_arrayetmek için başlatıcı ifadesinde çürütülen şey olarak başlatıldı - ve &pointer_to_array türüne char **(işaretçi a işaretçisi char).

Bunlardan: my_array(çürüme sonrasında char*) &my_arrayve pointer_to_arraytümü doğrudan diziye veya dizinin ilk öğesine işaret eder ve böylece aynı adres değerine sahiptir.


3

Bir dizinin bellek düzenine baktığınızda, aynı adresin neden my_arrayve &my_arraysonucunun kolayca anlaşılabilir.

Diyelim ki 10 karakterlik bir diziniz var (bunun yerine kodunuzdaki 100 karakter).

char my_array[10];

İçin bellek my_arrayşuna benzer:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

C / C ++ 'da bir dizi, bir ifadedeki ilk öğenin imlecine işaret eder.

printf("my_array = %p\n", my_array);

Dizinin ilk öğesinin nerede olduğunu incelerseniz, adresinin dizinin adresiyle aynı olduğunu görürsünüz:

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].

3

C'nin hemen öncüsü olan B programlama dilinde, işaretçiler ve tamsayılar serbestçe değiştirilebilirdi. Sistem, tüm bellek dev bir diziymiş gibi davranırdı. Her değişken adının kendisiyle ilişkilendirilmiş genel veya yığınla ilişkili bir adresi vardı, her değişken adı için derleyicinin izlemesi gereken tek şey, genel veya yerel bir değişken olup olmadığı ve ilk genel veya yerel ile ilgili adresi değişken.

Gibi küresel bir deklarasyon Verilen i;: [herşey bir tamsayı / işaretçi beri bir türünü belirtmek gerek vardı] derleyici olarak tarafından işlenmiş olur address_of_i = next_global++; memory[address_of_i] = 0;gibi ve bir bildiri i++olarak işlenecektir: memory[address_of_i] = memory[address_of_i]+1;.

Gibi bir bildirim arr[10];olarak işlenir address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;. Bu bildirim işlenir yapılmaz derleyicinin arrbir dizi olduğunu hemen unutabileceğini unutmayın . Gibi bir ifade arr[i]=6;olarak işlenir memory[memory[address_of_a] + memory[address_of_i]] = 6;. Derleyici, arrbir diziyi ve ibir tamsayıyı temsil etmeyi umursamaz ya da tam tersi olmaz. Aslında, her ikisinin de dizi veya tam sayı olması önemli değildir; ortaya çıkan davranışın muhtemelen faydalı olup olmadığına bakılmaksızın, kodu açıklandığı gibi mükemmel bir şekilde oluşturur.

C programlama dilinin amaçlarından biri B ile büyük ölçüde uyumlu olmaktı. B'de, bir dizinin adı [B'nin terminolojisinde "vektör" olarak adlandırılır], başlangıçta işaret etmek için atanmış bir işaretçi tutan bir değişken tanımladı verilen büyüklükte bir ayırmanın ilk öğesine gönderilir, böylece bu ad bir işlev için bağımsız değişken listesinde görünürse, işlev vektöre bir işaretçi alır. C, başlangıçta tahsise işaret eden bir işaretçi değişkeni yerine tahsisin adresi ile sıkı bir şekilde ilişkili olan "gerçek" dizi türlerini eklese de, dizilerin bir C-tipi dizinin aynı şekilde davrandığını belirten işaretçiler için kod ayrıştırması bir vektörü bildiren ve daha sonra adresini tutan değişkeni asla değiştirmeyen B koduna.


1

Aslında &myarrayve myarrayher ikisi de temel adres.

Kullanmak yerine farkı görmek istiyorsanız

printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);

kullanım

printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);
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.