C'de geçersiz işaretçi için işaretçi aritmetiği


176

Özel bir tipine karşı bir işaretçi (örneğin zaman int, char, float, ..) artırılır, değeri veri türü büyüklüğü artar. voidBoyut verilerini gösteren bir işaretçi xartırılırsa, xilerideki baytları nasıl işaret eder ? Derleyici xişaretçinin değerine nasıl katkıda bulunacağını nasıl biliyor ?



3
Soru, derleyicinin (/ çalışma zamanı) işaretçinin hangi tür nesneye ayarlandığını bildiğini varsayar ve boyutunu işaretçiye ekler. Bu tam bir yanlış anlamadır: sadece adresi bilir.
PJTraill

2
" voidBoyut verilerini gösteren bir işaretçi xartırılırsa, xbayt ileriye nasıl işaret eder ?" Öyle değil. Bu tür soruları olan insanlar neden sormadan önce bunları test edemezler - bilirsiniz, en azından gerçekten derlenip derlenmediğini kontrol ettikleri çıplak asgari seviyeye kadar. -1, bunun +100 ve -0 olduğuna inanamıyorum.
underscore_d

Yanıtlar:


287

Nihai sonuç: a üzerindeki aritmetik hem C hem de C ++ ' void*da yasa dışıdır .

GCC buna bir uzantı olarak izin verir, bkz. Aritmetik açık void- ve İşlev İşaretçileri (bu bölümün kılavuzun "C Uzantıları" bölümünün bir parçası olduğunu unutmayın). Clang ve ICC muhtemelen void*GCC ile uyumluluk amacıyla aritmetiğe izin verir . Diğer derleyiciler (MSVC gibi) aritmetiği açmaya izin vermez void*ve GCC, -pedantic-errorsbayrak belirtilirse veya bayrak belirtilirse buna izin vermez -Werror-pointer-arith(kod tabanınızın da MSVC ile derlenmesi gerekiyorsa bu bayrak yararlıdır).

C Standardı Konuşuyor

Alıntılar n1256 taslağından alınmıştır.

Standardın toplama işlemi ile ilgili açıklaması:

6.5.6-2: Ekleme için, her iki işlenen de aritmetik tipte olmalı ya da bir işlenen bir nesne tipine işaretçi ve diğeri de tamsayı tipinde olmalıdır.

Dolayısıyla, buradaki soru void*, bir "nesne tipine" işaretçi mi , yoksa eşdeğer olarak void"nesne tipine" mi ait olduğudur. "Nesne türü" tanımı:

6.2.5.1: Türler, nesne türlerine (nesneleri tam olarak açıklayan türler), işlev türlerine (işlevleri açıklayan türlere) ve eksik türlere (nesneleri tanımlayan ancak boyutlarını belirlemek için gereken bilgiye sahip olmayan türler ) bölünmüştür .

Ve standart şu voidşekilde tanımlar :

6.2.5-19: voidTür boş bir değer kümesi içerir; tamamlanamayan eksik bir tür.

Yana voidtamamlanmamış bir türüdür, bir nesne türü değil. Bu nedenle, toplama işlemi için geçerli bir işlenen değildir.

Bu nedenle, bir voidişaretçide işaretçi aritmetiği gerçekleştiremezsiniz .

notlar

Başlangıçta, void*C standardının bu bölümleri nedeniyle aritmetiğe izin verildiği düşünülüyordu :

6.2.5-27: Geçersiz bir işaretçi, bir karakter türüne işaretçi ile aynı temsil ve hizalama gereksinimlerine sahip olmalıdır.

Ancak,

Aynı temsil ve hizalama gereklilikleri, işlevlere argümanlar, işlevlerden değer döndürme ve sendika üyeleri olarak değiştirilebilirlik anlamına gelir.

Bu yollarla Yani printf("%s", x)bakılmaksızın aynı anlama sahiptir xtürü vardır char*ya void*, ama bir aritmetik yapabileceği anlamına gelmez void*.

Editörün notu: Bu cevap nihai sonucu yansıtacak şekilde düzenlendi.


7
C99 standardından: (6.5.6.2) Ekleme için, her iki işlenen de aritmetik tipe sahip olmalı veya bir işlenen bir nesne tipine işaretçi ve diğeri tamsayı tipine sahip olmalıdır. (6.2.5.19) Boşluk tipi boş bir değer kümesi içerir; tamamlanamayan eksik bir tür. void*İşaretçi aritmetiğine izin verilmediğini açıkça ortaya koyuyor . GCC'nin bunu yapmasına izin veren bir uzantısı var.
Meslek

1
cevabınızın artık yararlı olduğunu düşünmüyorsanız, silebilirsiniz.
caf

1
Bu cevap, geçersiz işaretçilerin aritmetik amaçlı olmadığına dair kesin kanıt içerdiğinden, yanlış olduğu kanıtlanmasına rağmen yararlıydı.
Ben Flynn

1
Bu iyi bir cevaptır, doğru sonuca ve gerekli alıntılara sahiptir, ancak bu soruya gelen insanlar cevabın dibini okumadıkları için yanlış sonuca vardılar. Bunu daha açık hale getirmek için düzenledim.
Dietrich Epp

1
Clang ve ICC void*aritmetiğe izin vermez (en azından varsayılan olarak).
Sergey Podobry

61

İşaretçilerde işaretçi aritmetiğine izin verilmez void*.


16
+1 İşaretçi aritmetiği yalnızca (tam) nesne türlerine işaretçiler için tanımlanır . voidBir olan tamamlanmamış tip tanımı gereği tamamlanacak olamaz.
schot

1
@schot: Kesinlikle. Ayrıca, işaretçi aritmetiği yalnızca bir dizi nesnesinin bir öğesinin işaretçisinde tanımlanır ve yalnızca işlemin sonucu aynı dizideki veya o dizinin son öğesinden sonraki bir öğeye işaretçi olacaksa tanımlanır. Bu koşullar karşılanmazsa, tanımlanmamış bir davranıştır. (From C99 standard 6.5.6.8)
Job

1
Görünüşe göre gcc 7.3.0 ile böyle değil. Derleyici p + 1024 değerini kabul eder, burada p geçersizdir *. Ve sonuç ((karakter *) p) + 1024 ile
aynıdır

@ zzz777 bir GCC uzantısı, en çok oy alan cevaptaki bağlantıya bakın.
Ruslan

19

karakter imlecine x işaretini ilerletir.


4
Neden rahatsız oluyorsun? Her zaman bilinmesi gereken doğru tipe yayınlayın ve bunu 1 ile artırın. Yeni değeri başka bir tür olarak tanımlamak char, arttırmak xve sonra yeniden yorumlamak hem anlamsız hem de tanımsız davranıştır.
underscore_d

9
Eğer göre olması man 3 qsortgereken sıralama fonksiyonu yazıyorsanız void qsort(void *base, size_t nmemb, size_t size, [snip]), o zaman "doğru tip" bilmek için bir yol var
alisianoi

15

C standart izin vermez boşluk işaretçi aritmetik. Ancak, GNU C büyüklüğü dikkate alınarak izin verilir boşluk DİR 1.

C11 standardı §6.2.5

Paragraf - 19

voidTürü değerleri boş bir dizi içerir; bir olan tamamlanmamış nesne türü tamamlanamaz.

Aşağıdaki program GCC derleyicide iyi çalışıyor.

#include<stdio.h>

int main()
{
    int arr[2] = {1, 2};
    void *ptr = &arr;
    ptr = ptr + sizeof(int);
    printf("%d\n", *(int *)ptr);
    return 0;
}

Diğer derleyiciler hata oluşturabilir.



8

İşaretçi aritmetiği yapmadan önce başka bir işaretçi türüne yayınlamanız gerekir.


7

Geçersiz işaretçiler herhangi bir bellek yığınına işaret edebilir. Bu nedenle derleyici, boş bir işaretçide işaretçi aritmetiği denediğimizde kaç baytın artırılacağını / azaltılacağını bilmez. Bu nedenle, boş işaretçiler, herhangi bir işaretçi aritmetiğine katılabilmeleri için önce bilinen bir türe ilk tip olarak yazılmalıdır.

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.

-1

Derleyici türünü bilir. Verilen void *x:

  • x+1bir bayt ekler x, işaretçi bayta giderx+1
  • (int*)x+1sizeof(int)bayt ekler , işaretçi bayta giderx + sizeof(int)
  • (float*)x+1adresler sizeof(float)bayt, vb.

İlk öğe taşınabilir değildir ve C / C ++ Galateo'ya karşıdır, yine de C dilinde doğrudur, yani muhtemelen uygun bir bayrak (-Wpointer-arith gibi) gerektiren birçok derleyicide bir şey derleyecektir.


2
Althought the first item is not portable and is against the Galateo of C/C++Doğru. it is nevertheless C-language-correctYanlış. Bu iki kez! İşaretçi aritmetiği void *sözdizimsel olarak yasadışıdır, derlenmemelidir ve varsa tanımsız davranış üretir. Dikkatsiz bir programcı bazı uyarıları devre dışı bırakarak derlemeyi yapabilirse, bu bir mazeret değildir.
underscore_d

@underscore_d: Sanırım bazı derleyiciler bir uzantı olarak izin vermek için kullanılır, çünkü unsigned char*örneğin sizeofbir işaretçiye bir değer eklemek için döküm yapmaktan çok daha uygun .
supercat
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.