... tahsis edilen aralığın dışında bir işaretçiyi azaltmak sadece bana çok kabataslak geliyor. C bu "izin verilen" davranış mı?
İzin? Evet. İyi bir fikir? Genellikle değil.
C, montaj dili için bir kısayoldur ve montaj dilinde işaretçiler yoktur, sadece bellek adresleri vardır. C'nin işaretçileri, aritmetiğe maruz kaldıklarında işaret ettiklerinin boyutuna göre artan veya azalan yan davranışa sahip bellek adresleridir. Bu, sözdizimi açısından aşağıdakilerin iyi olmasını sağlar:
double *p = (double *)0xdeadbeef;
--p; // p == 0xdeadbee7, assuming sizeof(double) == 8.
double d = p[0];
Diziler aslında C'de bir şey değildir; sadece diziler gibi davranan bitişik bellek aralıklarına işaret ediyorlar. []
Operatör böylece, işaretçi aritmetik yapıyor ve çözümleyecek bir kısaltmadır a[x]
aslında aracı *(a + x)
.
Yukarıdakileri yapmak için geçerli birkaç neden vardır, örneğin birkaç tane G / Ç cihazı double
0xdeadbee7
ve ile eşleştirilen0xdeadbeef
. Bunu yapmak için çok az program gerekir.
&
Operatörünü kullanmak veya aramak gibi bir şeyin adresini oluşturduğunuzdamalloc()
orijinal işaretçiyi olduğu gibi tutmak istersiniz, böylece işaret ettiği şeyin aslında geçerli bir şey olduğunu bilirsiniz. İşaretçiyi küçültmek, bazı hatalı kodların kodunu kaldırmaya çalışabilir, hatalı sonuçlar elde edebilir, bir şeyleri zorlayabilir veya ortamınıza bağlı olarak bir segmentasyon ihlali gerçekleştirebilir. Bu özellikle doğrudur malloc()
, çünkü free()
tüm halkanın gevşemesine neden olacak değiştirilmiş bir versiyona değil, orijinal değeri geçmeyi hatırlamaya kimin çağırdığını yüklersiniz.
C'de 1 tabanlı dizilere ihtiyacınız varsa, asla kullanılmayacak bir ek öğe ayırma pahasına güvenli bir şekilde yapabilirsiniz:
double *array_create(size_t size) {
// Wasting one element, so don't allow it to be full-sized
assert(size < SIZE_MAX);
return malloc((size+1) * sizeof(double));
}
inline double array_index(double *array, size_t index) {
assert(array != NULL);
assert(index >= 1); // This is a 1-based array
return array[index];
}
Bunun üst sınırı aşmaya karşı korumak için hiçbir şey yapmadığını, ancak bu işlemek için yeterince kolay olduğunu unutmayın.
Zeyilname:
C99 taslağından bazı bölüm ve ayetler (üzgünüm, bağlantı kurabileceğim tek şey bu):
§6.5.2.1.1, alt simge işleci ile kullanılan ikinci ("diğer") ifadenin tamsayı tipinde olduğunu belirtir. -1
bir tamsayıdır ve bu p[-1]
geçerli kılar ve dolayısıyla işaretçiyi de&(p[-1])
geçerli . Bu, o konumdaki belleğe erişmenin tanımlı davranış üreteceği anlamına gelmez, ancak imleç hala geçerli bir imleçtir.
§6.5.2.2, dizi alt simge operatörünün, öğe numarasını işaretçiye ekleme eşdeğeri olarak değerlendirdiğini söylüyor. p[-1]
eşdeğerdir *(p + (-1))
. Hala geçerli, ancak istenen davranışlara yol açmayabilir.
§6.5.6.8 diyor (benimki vurgu):
Bir tamsayı türüne sahip bir ifade bir işaretçiye eklendiğinde veya bir işaretçiden çıkarıldığında, sonuç işaretçi işleneninin türüne sahiptir.
... ifade eğer P
işaret i
inci bir dizi nesne, ifadelerin elemanı (P)+N
(eşit biçimde, N+(P)
) ve (P)-N
(burada N
değere sahiptir n
, ile) alanına sırasıyla i+n
inci ve
i−n
dizi nesne inci elemanları, bu ana kadar mesafede .
Bu, işaretçi aritmetiğinin sonuçlarının bir dizideki bir öğeye işaret etmesi gerektiği anlamına gelir. Aritmetiğin bir kerede yapılması gerektiği anlamına gelmez. Bu nedenle:
double a[20];
// This points to element 9 of a; behavior is defined.
double d = a[-1 + 10];
double *p = a - 1; // This is just a pointer. No dereferencing.
double e = p[0]; // Does not point at any element of a; behavior is undefined.
double f = p[1]; // Points at element 0 of a; behavior is defined.
Bir şeyleri bu şekilde yapmanızı tavsiye eder miyim? Yapmıyorum ve cevabım bunun nedenini açıklıyor.