C de negatif dizi indekslerine izin veriliyor mu?


115

Sadece bir kod okudum ve kişinin arr[-2]2. öğeye arr, şunun gibi, daha önce erişmek için kullandığını gördüm :

|a|b|c|d|e|f|g|
       ^------------ arr[0]
         ^---------- arr[1]
   ^---------------- arr[-2]

Buna izin var mı?

Bunun arr[x]ile aynı olduğunu biliyorum *(arr + x). Yani arr[-2]olduğu *(arr - 2)Tamam görünüyor. Ne düşünüyorsun?

Yanıtlar:


168

Bu doğru. C99 §6.5.2.1 / 2'den itibaren:

Alt simge operatörünün [] tanımı, E1 [E2] 'nin (* ((E1) + (E2))) ile aynı olmasıdır.

Sihir yok. 1-1 eşdeğerdir. Her zaman olduğu gibi, bir işaretçi (*) başvurusunu kaldırırken, geçerli bir adrese işaret ettiğinden emin olmanız gerekir.


2
Ayrıca, UB'yi almak için işaretçinin referansını kaldırmanız gerekmediğini unutmayın. Yalnızca hesaplama somearray-2, sonuç başlangıcından somearraysonunu 1 geçen aralıkta olmadığı sürece tanımsızdır .
RBerteig

34
Daha eski kitaplarda, işaretçi aritmetiği için []bir sözdizimi şekeri olarak bahsedilirdi . Yeni başlayanların kafasını karıştırmanın en sevdiğim yolu 1[arr]- yerine arr[1]- yazmak ve bunun ne anlama geldiğini tahmin etmelerini izlemektir.
Dummy00001

4
Negatif olan 32 bit int indeksiniz olduğunda 64 bit sistemlerde (LP64) ne olur? Adres hesaplamasından önce dizin 64 bitlik işaretli int'e yükseltilmeli mi?
Paul R

4
@Paul, §6.5.6 / 8'den (Toplama işleçleri), "Bir işaretçiye tamsayı türüne sahip bir ifade eklendiğinde veya çıkarıldığında, sonuç işaretçi işleninin türüne sahip olur. İşaretçi işleneni bir öğeyi işaret ediyorsa ve dizi yeterince büyükse, sonuç, ortaya çıkan ve orijinal dizi öğelerinin alt simgelerinin farkı tamsayı ifadesine eşit olacak şekilde orijinal öğeden uzaklığı olan bir öğeye işaret eder. " Bu yüzden yükseltileceğini ve ((E1)+(E2))beklenen değere sahip bir (64-bit) işaretçi olacağını düşünüyorum.
Matthew Flaschen

@Matthew: Bunun için teşekkürler - makul bir şekilde beklendiği gibi çalışması gerektiği gibi geliyor .
Paul R.

63

Bu yalnızca arrbir dizideki ikinci öğeyi veya sonraki bir öğeyi gösteren bir işaretçi ise geçerlidir . Aksi takdirde, dizinin sınırları dışındaki belleğe erişiyor olacağınız için geçerli değildir. Yani, örneğin, bu yanlış olur:

int arr[10];

int x = arr[-2]; // invalid; out of range

Ama bu sorun olmaz:

int arr[10];
int* p = &arr[2];

int x = p[-2]; // valid:  accesses arr[0]

Bununla birlikte, bir negatif alt simge kullanmak alışılmadık bir durumdur.


Geçersiz olduğunu söyleyecek kadar ileri gitmezdim, sadece potansiyel olarak dağınık
Matt Joiner

13
@Matt: İlk örnekteki kod, tanımsız davranışa neden olur.
James McNellis

5
Bu geçersiz. C standardına göre, açıkça tanımlanmamış davranışa sahiptir. Öte yandan, int arr[10];bir yapının kendisinden önce başka unsurlarla birlikte bir parçası olsaydı, arr[-2]potansiyel olarak iyi tanımlanabilirdi ve buna offsetof
dayanıp

4
Sona yakın K&R Bölüm 5.3'te If one is sure that the elements exist, it is also possible to index backwards in an array; p[-1], p[-2], and so on are syntactically legal, and refer to the elements that immediately precede p[0]. Of course, it is illegal to refer to objects that are not within the array bounds.buldum : Yine de, örneğiniz onu anlamama yardımcı olması açısından daha iyi. Teşekkürler!
Qiang Xu

4
İş parçacığı büyücülüğü için üzgünüm, ama K & R'nin "yasa dışı" nın ne anlama geldiği konusunda belirsizliğini seviyorum. Son cümle, sınır dışı erişimlerin bir derleme hatası atması gibi ses çıkarır. Bu kitap yeni başlayanlar için zehirdir.
Martin

12

Bana güzel geliyor. Bununla birlikte, meşru olarak buna ihtiyaç duyacağınız nadir bir durumdur.


9
Bu o kadar nadir değil - örneğin mahalle operatörleriyle görüntü işlemede çok kullanışlıdır.
Paul R

Bunu kullanmam gerekiyordu çünkü bir yığın ve yığın [yapı / tasarım] içeren bir bellek havuzu oluşturuyorum. Yığın daha yüksek bellek adreslerine doğru büyür, yığın daha düşük bellek adreslerine doğru büyür. Ortada buluşma.
JMI MADISON

8

Muhtemelen bu arr, dizinin ortasına işaret ediyordu , dolayısıyla arr[-2]sınırların dışına çıkmadan orijinal dizideki bir şeyi işaret ediyordu .


7

Bunun ne kadar güvenilir olduğundan emin değilim, ancak 64-bit sistemlerdeki (muhtemelen LP64) negatif dizi indeksleri hakkındaki şu uyarıyı okudum: http://www.devx.com/tips/Tip/41349

Yazar, 64 bit adresli 32 bit int dizi indekslerinin, dizi indeksi açıkça 64 bite yükseltilmedikçe (örneğin bir ptrdiff_t dönüşümü aracılığıyla) kötü adres hesaplamalarıyla sonuçlanabileceğini söylüyor gibi görünüyor. Aslında gcc 4.1.0'ın PowerPC sürümünde doğası gereği bir hata gördüm, ancak bunun bir derleyici hatası mı (yani C99 standardına göre çalışmalı) veya doğru davranış mı (yani dizinin 64'e dönüştürülmesi gerekiyor) bilmiyorum doğru davranış için bitler)?


3
Bu bir derleyici hatası gibi geliyor.
tbleher

2

Sorunun cevaplandığını biliyorum ama bu açıklamayı paylaşmaya dayanamadım.

Derleyici tasarımının ilkelerini hatırlıyorum, a'nın bir int dizisi olduğunu ve int boyutunun 2 olduğunu ve a için temel adresin 1000 olduğunu varsayalım.

Nasıl a[5]çalışacak ->

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Bu açıklama aynı zamanda dizilerdeki negatif indekslerin C de çalışmasının sebebidir.

Yani erişirsem a[-5]bana verecek

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

990 konumunda bana nesne döndürecektir. Bu mantıkla C'deki Array'deki negatif indekslere erişebiliriz.


2

Birinin neden negatif indeks kullanmak isteyeceği hakkında, onları iki bağlamda kullandım:

  1. Size tarak [1] [- 1] = 0'ı söyleyen bir kombinatoryal sayılar tablosuna sahip olmak; tabloya erişmeden önce dizinleri her zaman kontrol edebilirsiniz, ancak bu şekilde kod daha temiz görünür ve daha hızlı çalışır.

  2. Masanın başına bir centinel koymak. Örneğin, aşağıdaki gibi bir şey kullanmak istiyorsunuz

     while (x < a[i]) i--;

ama sonra ibunun olumlu olup olmadığını da kontrol etmelisiniz .
Çözüm: böylece bunu yapmak a[-1]olduğunu -DBLE_MAX, böylece x&lt;a[-1]her zaman false olur.


0
#include <stdio.h>

int main() // negative index
{ 
    int i = 1, a[5] = {10, 20, 30, 40, 50};
    int* mid = &a[5]; //legal;address,not element there
    for(; i < 6; ++i)
    printf(" mid[ %d ] = %d;", -i, mid[-i]);
}

1
Bu kod soruyu yanıtlayabilirken, bu kodun soruyu neden ve / veya nasıl yanıtladığına ilişkin ek bağlam sağlamak, uzun vadeli değerini artırır.
β.εηοιτ.βε

Harika Python ... al onları. Basit bir kullanım durumu, birçok Proje durumunda çok gerçek bir gereksinim olan, dizi boyutunu bilmeden bir dizinin son öğesine erişilebilmesidir. Ayrıca birçok DSL bundan faydalanmaktadır.
Rathinavelu Muthaliar
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.