Kaç seviye işaretçimiz olabilir?


443

*Tek bir değişkente kaç tane ( ) işarete izin verilir?

Aşağıdaki örneği ele alalım.

int a = 10;
int *p = &a;

Benzer şekilde

int **q = &p;
int ***r = &q;

ve bunun gibi.

Örneğin,

int ****************zz;

582
Bu senin için gerçek bir sorun haline gelirse, çok yanlış bir şey yapıyorsun.
ThiefMaster

279
Beyniniz patlayana veya derleyici eriyene kadar işaretçiler eklemeye devam edebilirsiniz - hangisi en erken olursa.
JeremyP

47
Bir işaretçiye bir işaretçi tekrar, sadece bir işaretçi olduğundan, herhangi bir teorik sınır olmamalıdır. Belki derleyici gülünç yüksek sınırın ötesinde başa çıkamayacak, ama iyi ...
Christian Rau

73
en yeni c ++ ile böyle bir şey kullanmalısınızstd::shared_ptr<shared_ptr<shared_ptr<...shared_ptr<int>...>>>
josefx

44
@josefx - bu C ++ standardında bir sorun olduğunu gösterir - akıllı göstergeleri güçlere yükseltmenin bir yolu yoktur. Derhal destek (pow (std::shared_ptr, -0.3))<T> x;için bir uzatma talep etmeliyiz, örneğin -0.3 dolaylı dolaylama düzeyi.
Steve314

Yanıtlar:


400

CStandart belirtir alt sınır:

5.2.4.1 Çeviri sınırları

276 Uygulama, aşağıdaki sınırların her birinin en az bir örneğini içeren en az bir programı çevirebilir ve yürütebilir: [...]

279 - 12 bir bildirimdeki aritmetik, yapı, birleşim veya boşluk türünü değiştiren işaretçi, dizi ve işlev bildiricileri (herhangi bir kombinasyonda)

Üst sınır uygulamaya özgüdür.


121
C ++ standardı, bir uygulama desteğinin en az 256 olmasını "önerir". (Okunabilirlik, 2 veya 3'ü aşmamanızı önerir ve hatta o zaman bile: birden fazla istisnai olmalıdır.)
James Kanze

22
Bu sınır, tek bir bildiride kaç tanesiyle ilgilidir; birden fazla typedefs ile ne kadar dolaylı yol elde edebileceğinize bir üst sınır getirmez .
Kaz

11
@Kaz - evet, bu doğru. Ancak özellik zorunlu bir alt sınır belirttiği için (pun amaçlı değildir), tüm spec uyumlu derleyicilerin desteklemesi gereken etkin üst sınırdır . Elbette satıcıya özgü üst sınırdan daha düşük olabilir. (OP soru ile hizalamak için) farklı rephrased, öyle maksimum spec izin (başka bir şey satıcıya özgü olacaktır.) Bir teğet ısırıp, programcılar gerekir tedavi (genel durumda en azından) bu kadar onların üst sınır (satıcıya özgü üst sınırlara güvenmek için geçerli bir nedenleri olmadıkça) ... bana göre.
luis.espinal

5
Başka bir notta, uzun göt dereferencing zincirleri olan kodla çalışmak zorunda kalsaydım kendimi kesmeye
başlardım

11
@beryllium: Genellikle bu rakamlar bir ön standardizasyon yazılımı araştırmasından kaynaklanmaktadır. Bu durumda, muhtemelen ortak C programlarına ve mevcut C derleyicilerine baktılar ve 12'den fazla sorunla karşılaşacak en az bir derleyici buldular ve / veya 12 ile sınırlandırırsanız kırılan program yok.

155

Aslında, C programları genellikle sonsuz işaretçi dolaylı işlevini kullanır. Bir veya iki statik seviye ortaktır. Üçlü dolaylama nadirdir. Fakat sonsuzluk çok yaygındır.

Sonsuz işaretçi dolaylaması, elbette imkansız olacak doğrudan bir bildirici ile değil, bir yapı yardımıyla elde edilir. Ve bu yapıya, sonlandırılabilecek farklı düzeylerde başka veriler ekleyebilmeniz için bir yapıya ihtiyaç vardır.

struct list { struct list *next; ... };

şimdi sahip olabilirsiniz list->next->next->next->...->next. Bu gerçekten sadece çoklu işaretçi indirections geçerli: *(*(..(*(*(*list).next).next).next...).next).next. Ve .nexttemelde yapının ilk üyesi olduğunda bir noop, bu yüzden bunu hayal edebiliriz ***..***ptr.

Bu konuda gerçekten bir sınır yoktur, çünkü bağlantılar böyle dev bir ifade yerine bir döngü ile geçilebilir ve dahası, yapı kolayca dairesel hale getirilebilir.

Başka bir deyişle, bağlantılı listeler, bir sorunu çözmek için başka bir dolaylama düzeyi eklemenin nihai örneği olabilir, çünkü her itme işleminde dinamik olarak yapıyorsunuz. :)


48
Ancak bu tamamen farklı bir konudur - başka bir yapıya bir işaretçi içeren bir yapı, bir işaretçi-işaretçiden çok farklıdır. Int *****, int **** 'dan farklı bir türdür.
kabarık

12
"Çok" farklı değil. Fark kabarık. Sözdizimine anlambilimden daha yakındır. İşaretçi nesnesine işaretçi veya işaretçi içeren yapı nesnesine işaretçi? Aynı şey. Bir listenin onuncu unsuruna ulaşmak, dolaylı adresleme on seviyesidir. (Tabi ki, sonsuz bir yapı ifade etme yeteneği, böylece eksik yapı türü ile kendisine noktasına mümkün yapı tipine bağlıdır list->nextve list->next->nextaynı tip, aksi takdirde sonsuz bir tür oluşturmak gerekir.)
Kaz

34
"Kabarık" kelimesini kullandığımda adının kabarık olduğunu fark etmedim. Bilinçaltı etkisi? Ama daha önce bu şekilde kullandığımdan eminim.
Kaz

3
Ayrıca, makine dilinde, LOAD R1, [R1]R1'in her adımda geçerli bir işaretçi olduğu sürece tekrarlayabileceğinizi unutmayın . "Adresi tutan kelime" dışında herhangi bir tür bulunmamaktadır. Bildirilen türlerin olup olmadığı, dolaylamayı ve kaç seviyeye sahip olduğunu belirlemez.
Kaz

4
Yapı daireselse değil. Eğer R1kendisine noktaları daha sonra bir konumun adresini tutan LOAD R1, [R1]sonsuz döngüye yürütülebilir.
Kaz

83

Teorik:

İstediğiniz kadar çok sayıda dolaylama düzeyine sahip olabilirsiniz.

Pratikte:

Tabii ki, bellek tüketen hiçbir şey belirsiz olamaz, ana bilgisayar ortamında mevcut olan kaynaklar nedeniyle sınırlamalar olacaktır. Dolayısıyla pratik olarak bir uygulamanın neleri destekleyebileceğine dair bir maksimum sınır vardır ve uygulama bunu uygun şekilde belgelendirmelidir. Bu nedenle, tüm bu tür eserlerde, standart maksimum limiti belirtmez, ancak alt limitleri belirtir.

İşte referans:

C99 Standart 5.2.4.1 Çeviri sınırları:

- Bir bildirimde aritmetik, yapı, birleşim veya boşluk türünü değiştiren 12 işaretçi, dizi ve işlev bildiricisi (herhangi bir kombinasyonda).

Bu, her uygulamanın desteklemesi gereken alt sınırı belirtir . Bir dipnotta standardın ayrıca şunları söylediğine dikkat edin:

18) Uygulamalar mümkün olduğunda sabit çeviri sınırları koymaktan kaçınmalıdır.


16
indirections herhangi bir yığın taşmaz!
Basile Starynkevitch

1
Düzeltildi, fonksiyona iletilen parametrelerin limiti olarak q'yu okuma ve cevaplama gibi hatalı bir duygum vardı. Neden bilmiyorum ?!
Alok Kaydet

2
@basile - Yığın derinliğinin ayrıştırıcıda bir sorun olmasını beklerdim. Birçok resmi ayrıştırma algoritmasının kilit bileşeni olarak bir yığını vardır. Çoğu C ++ derleyicisi muhtemelen bir özyinelemeli iniş çeşidi kullanır, ancak bu bile işlemci yığınına dayanır (veya bilgili olarak, işlemci yığını varmış gibi davranan dile) dayanır. Dilbilgisi kurallarının daha fazla yuvalanması daha derin bir yığın anlamına gelir.
Steve314

8
indirections herhangi bir yığın taşmaz! -> Hayır! ayrıştırıcı yığını taşabilir. Yığın işaretçi dolaylamasıyla nasıl bir ilişkisi var? Ayrıştırıcı yığını!
Pavan Manjunath

Eğer *arka arkaya sınıfları sayısı için aşırı ve her yüklenme sıradaki diğer tür bir nesnenin döner, daha sonra zincirli fonksiyon aramalar için stackoverflow olabilir.
Nawaz

76

İnsanların dediği gibi, "teoride" sınır yoktur. Ancak, ilgisiz bunu g ++ 4.1.2 ile çalıştırdım ve 20.000'e kadar büyüklükte çalıştı. Derleme rağmen oldukça yavaş, bu yüzden daha yüksek deneyin vermedi. Yani g ++ 'ın da herhangi bir sınır koymadığını tahmin ediyorum. ( size = 10Hemen görünmüyorsa ptr.cpp dosyasını ayarlamayı ve aramayı deneyin .)

g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr

create.cpp

#include <iostream>

int main()
{
    const int size = 200;
    std::cout << "#include <iostream>\n\n";
    std::cout << "int main()\n{\n";
    std::cout << "    int i0 = " << size << ";";
    for (int i = 1; i < size; ++i)
    {
        std::cout << "    int ";
        for (int j = 0; j < i; ++j) std::cout << "*";
        std::cout << " i" << i << " = &i" << i-1 << ";\n";
    }
    std::cout << "    std::cout << ";
    for (int i = 1; i < size; ++i) std::cout << "*";
    std::cout << "i" << size-1 << " << \"\\n\";\n";
    std::cout << "    return 0;\n}\n";
    return 0;
}

72
Denediğimde 98242'den fazla alamadım. (Python'da senaryoyu yaptım, *başarısız olanı ve geçen öncekini iki katına çıkardım; daha sonra başarısız olan ilk aralık için ikili bir arama yaptım. Tüm test bir saniyeden az sürdü çalıştırmak için).
James Kanze

63

Kontrol etmek eğlenceli geliyor.

  • Visual Studio 2010 (Windows 7'de), bu hatayı almadan önce 1011 düzeyine sahip olabilirsiniz:

    önemli hata C1026: ayrıştırıcı yığını taşması, program çok karmaşık

  • gcc (Ubuntu), 100k + *çarpışma olmadan! Sanırım donanım buradaki sınır.

(sadece değişken bir bildirim ile test edilmiştir)


5
Gerçekten de, tekli operatörler için üretimler sağ-özyinelemeli, yani bir vardiya azaltma ayrıştırıcısı *bir azaltma yapmadan önce tüm düğümleri yığına kaydırır .
Kaz

28

Sınır yok, örneği buradan kontrol edin .

Cevap, "işaretçi seviyeleri" ile ne demek istediğinize bağlıdır. "Tek bir bildiride kaç dolaylı dolaylama düzeyine sahip olabilirsiniz?" cevap "En az 12" dir.

int i = 0;

int *ip01 = & i;

int **ip02 = & ip01;

int ***ip03 = & ip02;

int ****ip04 = & ip03;

int *****ip05 = & ip04;

int ******ip06 = & ip05;

int *******ip07 = & ip06;

int ********ip08 = & ip07;

int *********ip09 = & ip08;

int **********ip10 = & ip09;

int ***********ip11 = & ip10;

int ************ip12 = & ip11;

************ip12 = 1; /* i = 1 */

"Programın okunması zorlaşmadan önce kaç seviye işaretçi kullanabilirsiniz" demek istiyorsanız, bu bir zevk meselesidir, ancak bir sınır vardır. İki dolaylı seviyeye sahip olmak (bir şeye işaretçi) bir şeydir. Bundan daha fazlasını kolayca düşünmek biraz zorlaşır; alternatif daha kötü olmazsa yapmayın.

"Çalışma zamanında kaç düzey işaretçi indirgenmesi yapabileceğinizi" kastediyorsanız, sınır yoktur. Bu nokta, her düğümün bir sonrakini işaret ettiği dairesel listeler için özellikle önemlidir. Programınız işaretçileri sonsuza kadar takip edebilir.


7
Derleyici neredeyse sınırlıdır, çünkü derleyici bilgileri sınırlı miktarda bellekte tutmak zorundadır. ( g++makinemde 98242'de dahili bir hata ile iptal ediliyor. Gerçek sınırın makineye ve yüke bağlı olacağını düşünüyorum. Ayrıca bunun gerçek kodda bir sorun olmasını beklemiyorum.)
James Kanze

2
Evet @MatthieuM. : Sadece teorik olarak düşündüm :) Cevabı tamamladığınız için teşekkürler James
Nandkumar Tekale

3
Bağlantılı listeler gerçekten bir işaretçiye işaretçi değildir, bir işaretçi içeren bir
yapıya

1
@ Random832: Nand 'Eğer "Çalışma zamanında kaç tane işaretçi indirgenmesi olabileceğini" kastediyorsanız, sadece işaretçilerden (* n) işaretçilerden bahsetmenin kısıtlamasını açıkça kaldırıyordu.
LarsH

1
Demek istediğim şu anlama gelmiyor: ' Sınır yok, örneği buradan kontrol edin. 'Örnek, sınırın olmadığının kanıtı değil. Sadece 12 yıldızlı bir dolaylamanın mümkün olduğunu kanıtlıyor. Her ikisi circ_listde OP'nin sorusuyla ilgili herhangi bir örnek kanıtlamaz : Bir işaretçi listesinden geçebilmeniz, derleyicinin n-yıldız dolaylamasını derleyebileceği anlamına gelmez.
Alberto

24

Aslında işlev işaretçisi ile daha komik.

#include <cstdio>

typedef void (*FuncType)();

static void Print() { std::printf("%s", "Hello, World!\n"); }

int main() {
  FuncType const ft = &Print;
  ft();
  (*ft)();
  (**ft)();
  /* ... */
}

Gösterildiği gibi burada bu verir:

Selam Dünya!
Selam Dünya!
Selam Dünya!

Ve herhangi bir çalışma zamanı ek yükü içermez, böylece derleyiciniz dosyayı boşalana kadar onları istediğiniz kadar istifleyebilirsiniz.


20

Orada sınır yok . İşaretçi, içeriği bir adres olan bir bellek yığınıdır.
Dediğin gibi

int a = 10;
int *p = &a;

İşaretçiye işaretçi aynı zamanda başka bir işaretçinin adresini içeren bir değişkendir.

int **q = &p;

İşte qadresini tutan işaretçi işaretçisi pzaten adresini tutan hangi a.

Bir işaretçiye gösterici hakkında özel bir şey yoktur.
Yani başka bir işaretçinin adresini tutan poniterler zincirinde bir sınır yoktur.
yani.

 int **************************************************************************z;

izin verilir.


17

Her C ++ geliştiricisi ünlü üç yıldızlı programcıyı duymuş olmalı

Ve gerçekten de kamufle edilmesi gereken sihirli bir "işaretçi bariyeri" var gibi görünüyor

C2'den alıntı:

Üç Yıldızlı Programcı

C-programcıları için bir derecelendirme sistemi. İşaretçileriniz ne kadar dolaylıysa (yani değişkenlerinizden önce "*" o kadar çok), itibarınız o kadar yüksek olur. Yıldız olmayan C-programcıları neredeyse hiç yoktur, çünkü neredeyse tüm önemsiz olmayan programlar işaretçilerin kullanılmasını gerektirir. Çoğu bir yıldızlı programcılar. Eski zamanlarda (ben gencim, bu yüzden en azından benim için eski zamanlara benziyor), bazen üç yıldızlı bir programcı tarafından yapılan bir kod parçası ve huşu ile ürperti. Bazı insanlar, birden fazla dolaylama seviyesinde, işlev işaretçileriyle birlikte üç yıldızlı kod gördüklerini iddia ettiler. Bana UFO'lar kadar gerçek geliyordu.


2
github.com/psi4/psi4public/blob/master/src/lib/libdpd/… ve benzerleri 4 yıldızlı bir programcı tarafından yazıldı. Aynı zamanda benim bir arkadaşım ve kodu yeterince okursanız, neden 4 yıldıza layık olduğunu anlayacaksınız.
Jeff

13

Burada iki olası soru olduğunu unutmayın: C tipinde kaç tane işaretçi dolaylaması seviyesi elde edebileceğimizi ve tek bir bildiricide kaç tane işaretçi dolaylaması düzeyi gerçekleştirebileceğimizi unutmayın.

C standardı, birincisine maksimumun uygulanmasına izin verir (ve bunun için minimum bir değer verir). Ancak bu, birden fazla typedef bildirimi ile atlatılabilir:

typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */

Sonuçta bu, bir C programının reddedilmeden önce ne kadar büyük / karmaşık yapılabileceği fikrine bağlı bir uygulama meselesidir, ki bu çok derleyiciye özgüdür.


4

Rastgele sayıda * olan bir tür üretmenin şablon meta programlaması ile gerçekleşebilecek bir şey olduğunu belirtmek isterim. Tam olarak ne yaptığımı unuttum, ancak yinelemeli T * tiplerini kullanarak aralarında bir tür meta manevra yapan yeni farklı tipler üretebileceğim önerildi .

Şablon Meta programlaması çılgınlığa yavaş bir iniştir, bu nedenle birkaç bin dolaylı seviyeye sahip bir tür oluştururken mazeret yapmak gerekli değildir. Bu, örneğin, işlevsel bir dil olarak şablon genişletmeye peano tam sayılarını eşlemenin kullanışlı bir yoludur.


Cevabınızı tam olarak anlamadığımı itiraf ediyorum, ancak bana araştırmam için yeni bir alan verildi. :)
ankush981

3

2004 MISRA C standardının 17.5 numaralı kuralı , 2'den fazla işaretçi dolaylaması düzeyini yasaklamaktadır.


15
Bunun derleyiciler için değil programcılar için bir tavsiye olduğuna eminim.
Cole Johnson

3
Belgeyi 17.5 kuralıyla 2'den fazla işaretçi dolaylaması düzeyi hakkında okudum. Ve mutlaka 2'den fazla seviyeyi yasaklaması gerekmez. 2'den fazla seviye "non-compliant"standartlarına uygun olduğu için kararın takip edilmesi gerektiğini belirtmektedir . Kararlarında önemli olan kelime veya cümle bu ifadedeki kelimenin kullanılmasıdır "should": Use of more than 2 levels of indirection can seriously impair the ability to understand the behavior of the code, and should therefore be avoided.Bunlar, dil standardı tarafından belirlenen kuralların aksine, bu kuruluş tarafından belirlenen kılavuz ilkelerdir.
Francis Cugler

1

Gerçek sınır diye bir şey yoktur ama sınır vardır. Tüm işaretçiler genellikle yığın değil yığınta depolanan değişkenlerdir . Yığın genellikle küçüktür (bazı bağlantılar sırasında boyutunu değiştirmek mümkündür). Diyelim ki 4MB'lık bir yığınınız var, oldukça normal boyutta. Diyelim ki 4 bayt boyutunda bir işaretçi var (işaretçi boyutları mimariye, hedefe ve derleyici ayarlarına bağlı olarak aynı değildir).

Bu durumda 4 MB / 4 b = 1024mümkün olan maksimum sayı 1048576 olur, ancak diğer bazı şeylerin yığın halinde olduğu gerçeğini göz ardı etmemeliyiz.

Bununla birlikte, bazı derleyiciler maksimum işaretçi zinciri sayısına sahip olabilir, ancak sınır yığın boyutudur. Sonsuzluk ile bağlantı sırasında yığın boyutunu artırırsanız ve sınırsız işaretçi zincirine sahip olacak şekilde işletim sistemini çalıştıran sonsuz belleği olan bir makineye sahip olursanız.

int *ptr = new int;İşaretçinizi kullanır ve yığın içine koyarsanız, bu olağan bir yol sınırı yığın değil yığın boyutu olacaktır.

DÜZENLE Sadece bunu fark et infinity / 2 = infinity. Makinede daha fazla bellek varsa, işaretçi boyutu artar. Eğer bellek sonsuz ve işaretçi boyutu sonsuz ise, bu yüzden kötü haber ... :)


4
A) İşaretçiler yığın ( new int*) üzerinde saklanabilir . B) En azından makul mimarilerde bir int*ve bir int**********aynı büyüklüğe sahiptir.

@rightfold A) Evet işaretçiler yığın halinde saklanabilir. Ancak, bir sonraki önceki işaretçiye işaret eden şeyleri tutan konteyner oluşturmak gibi çok farklı bir şey olurdu. B) Tabii ki int*ve int**********aynı boyutta, farklı olduklarını söylemedim.
ST3

2
Sonra yığın boyutunun uzaktan nasıl alakalı olduğunu görmüyorum.

@rightfold Tüm veriler yığın halinde olduğunda ve yığınta sadece bu verilere işaret ettiğinde her zamanki veri dağıtım yolunu düşünüyorum . Her zamanki gibi olurdu , ancak işaretçileri yığına koymanın mümkün olduğunu kabul ediyorum.
ST3

"Elbette int * ve bir int ********** aynı boyuta sahiptir" - standart bunu garanti etmez (her ne kadar doğru olmadığı hiçbir platform bilmiyorum).
Martin Bonner Monica

0

İşaretçileri sakladığınız yere bağlıdır. Eğer yığın halinde ise oldukça düşük bir sınırınız var. Öbekte saklarsanız, limitiniz çok daha yüksektir.

Şu programa bakın:

#include <iostream>

const int CBlockSize = 1048576;

int main() 
{
    int number = 0;
    int** ptr = new int*[CBlockSize];

    ptr[0] = &number;

    for (int i = 1; i < CBlockSize; ++i)
        ptr[i] = reinterpret_cast<int *> (&ptr[i - 1]);

    for (int i = CBlockSize-1; i >= 0; --i)
        std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl;

    return 0;
}

1M işaretçileri oluşturur ve zincirin ilk değişkene neyin gittiğini fark etmenin ne kadar kolay olduğunu gösterir number.

BTW. 92KRAM kullanır , bu yüzden ne kadar derine gidebileceğinizi hayal edin.

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.