Boşluk * ne demektir ve nasıl kullanılır?


152

Bugün başkalarının kodunu okurken şöyle bir şey gördüm void *func(void* i);, void*burada sırasıyla işlev adı ve değişken türü için bu ne anlama geliyor?

Ek olarak, bu tür bir işaretçiyi ne zaman ve nasıl kullanmalıyız?


2
Hangi C kitabını kullanıyorsunuz? Bütün bir bölümün daha iyi kısmını istiyorsun.
cnicutar




Bir işaret almak mallocve calloc. Man sayfası şöyle devam eder: "... herhangi bir yerleşik veri türü için uygun şekilde hizalanmış olan ayrılmış belleğe bir işaretçi döndür."
automaton

Yanıtlar:


181

İşaretçi void, "genel" bir işaretçi türüdür. A void *, açık bir atama olmadan başka herhangi bir işaretçi tipine dönüştürülebilir. Onunla bir void *işaretçi aritmetiği yapamazsınız veya bir işaretçi aritmetiği yapamazsınız; önce onu tam bir veri türüne bir işaretçiye dönüştürmelisiniz.

void *genellikle aynı kodda farklı işaretçi türleriyle çalışabilmeniz gereken yerlerde kullanılır. Yaygın olarak alıntı yapılan bir örnek, kütüphane işlevidir qsort:

void qsort(void *base, size_t nmemb, size_t size, 
           int (*compar)(const void *, const void *));

basebir dizinin adresidir, dizideki nmemböğelerin sayısıdır, sizeher öğenin boyutudur comparve dizinin iki öğesini karşılaştıran bir işleve göstericidir. Şöyle çağrılır:

int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);

Dizi ifadeleri iArr, dArrve lArrişlev çağrısında örtülü olarak dizi türlerinden işaretçi türlerine dönüştürülür ve her biri örtük olarak "göstericiden int/ double/ long" ye "işaretçi void" ye dönüştürülür.

Karşılaştırma işlevleri şuna benzer:

int compareInt(const void *lhs, const void *rhs)
{
  const int *x = lhs;  // convert void * to int * by assignment
  const int *y = rhs;

  if (*x > *y) return 1;
  if (*x == *y) return 0;
  return -1;
}

Kabul ederek void *, qsorther türden diziyle çalışabilir.

Kullanmanın dezavantajı, void *yazım güvenliğini pencereden dışarı ve yaklaşan trafiğe atmanızdır. Sizi yanlış karşılaştırma rutinini kullanmaktan koruyacak hiçbir şey yok:

qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);

compareIntargümanlarının ints'yi göstermesini bekliyor , ancak aslında doubles ile çalışıyor . Derleme sırasında bu sorunu yakalamanın bir yolu yoktur; sadece yanlış sıralı bir dizi ile sonuçlanacaksınız.


5
Aslında void*a'nın bir işlev işaretçisine dönüştürülebileceği garanti edilmez . Ancak veri işaretçileri için söylediğiniz şey geçerli.
Vatine

Void işaretçileri mevcut olmadan önce onun yerine "char *" kullanıldı. Ama boşluk daha iyidir, çünkü aslında herhangi bir şeyi doğrudan değiştirmek için kullanılamaz.
user50619

23

Bir void * kullanmak, işlevin belirli bir tür olması gerekmeyen bir işaretçi alabileceği anlamına gelir. Örneğin, soket işlevlerinde,

send(void * pData, int nLength)

bu, pek çok şekilde arayabileceğiniz anlamına gelir

char * data = "blah";
send(data, strlen(data));

POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));

Yani bu, diğer dillerdeki jeneriklere çok benziyor, ancak tür kontrolü yok, değil mi?
Winger Sendon

4
Sanırım benzer olacaktır, ancak tür denetimi olmadığından, hata yapmak çok garip sonuçlara neden olabilir veya programın tamamen çökmesine neden olabilir.
TheSteve

9

C bu açıdan dikkate değerdir. Söyleyebileceğim tek voidhiçlik olduğu void*(her şey olabilir) her şeydir.

*Farkı yaratan sadece bu kadar küçük .

Rene işaret etti. A void *, bir konum için bir İşaretçidir. Nasıl "yorumlanacağı" kullanıcıya bırakılmıştır.

C'de opak türlere sahip olmanın tek yolu budur. Çok belirgin örnekler, örneğin glib veya genel veri yapısı kitaplıklarında bulunabilir. "C Arayüzleri ve uygulamalarında" çok ayrıntılı olarak ele alınmıştır.

Tam bölümü okumanızı ve "elde etmek" için bir işaretçi kavramını anlamaya çalışmanızı öneririm.


7
void*

'hangi tipin depolandığı varsayımları olmaksızın belleğe bir göstericidir'. Örneğin, işlevine bir bağımsız değişken iletmek istiyorsanız ve bu bağımsız değişken birkaç türde olabilir ve işlevde her türü ele alırsınız.


3

İşaretçilerle ilgili bu makaleye göz atabilir http://www.cplusplus.com/doc/tutorial/pointers/ ve şu bölümü okuyabilirsiniz: boşluk işaretçileri .

Bu aynı zamanda C dili için de geçerlidir.

Boş işaretçi türü özel bir işaretçi türüdür. C ++ 'da void, türün yokluğunu temsil eder, bu nedenle void işaretçileri, türü olmayan bir değere işaret eden işaretçilerdir (ve dolayısıyla ayrıca belirsiz bir uzunluk ve belirlenmemiş özümleme özellikleri).

Bu, void işaretçilerinin bir tamsayı değerinden veya kayan değerden karakter dizisine kadar herhangi bir veri türünü işaret etmesine izin verir. Ancak karşılığında büyük bir sınırlamaları vardır: onlar tarafından gösterilen veriler doğrudan referans alınamaz (bu mantıklıdır, çünkü başvurulacak türümüz yoktur) ve bu nedenle adresi her zaman boş göstericiye çevirmek zorunda kalacağız. referansını kaldırmadan önce somut bir veri türüne işaret eden başka bir işaretçi türü.


3

Boş gösterici, genel işaretçi olarak bilinir. Örnek bir pthread senaryosu ile açıklamak istiyorum.

Thread fonksiyonu prototipe sahip olacaktır:

void *(*start_routine)(void*)

Pthread API tasarımcıları, evre işlevinin bağımsız değişkenini ve dönüş değerlerini değerlendirdi. Bu şeyler jenerik yapılırsa, argüman olarak gönderirken void * 'e cast yazabiliriz. benzer şekilde dönüş değeri void * 'dan alınabilir (Ama ben hiçbir zaman thread fonksiyonundan dönüş değerleri kullanmadım).

void *PrintHello(void *threadid)
{
   long tid;

   // ***Arg sent in main is retrieved   ***
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main (int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0; t<NUM_THREADS; t++){
      //*** t will be type cast to void* and send as argument.
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);   
      if (rc){
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }    
   /* Last thing that main() should do */
   pthread_exit(NULL);
}

Neden ana yolun sonunda pthread_exit(NULL);yerine return 0;arayasınız?
Seabass77


1

a void*bir göstericidir, ancak işaret ettiği şeyin türü belirtilmemiştir. Bir işleve bir geçersiz gösterici ilettiğinizde, onu kullanmak için daha sonra işlevde bu doğru türe geri döndürmek için türünün ne olduğunu bilmeniz gerekir. pthreadsÖrneğinizde iş parçacığı işlevi olarak kullanılan prototipi tam olarak kullanan işlevleri kullanan örnekler göreceksiniz . Daha sonra void*bağımsız değişkeni, seçtiğiniz genel bir veri türüne işaretçi olarak kullanabilir ve ardından iş parçacığı işlevinizde kullanmak için bu türe geri dönüştürebilirsiniz. Void işaretçileri kullanırken dikkatli olmalısınız, ancak gerçek türündeki bir işaretçiye geri dönmediğiniz sürece her türlü problemle karşılaşabilirsiniz.


1

C11 standardı (n1570) §6.2.2.3 al1 p55 diyor ki:

İşaretçi void, bir işaretçiye veya işaretçiden herhangi bir nesne türüne dönüştürülebilir. Herhangi bir nesne türüne bir işaretçi, bir işaretçiye void'e ve tekrar geri dönüştürülebilir; sonuç orijinal göstericiye eşit olmalıdır.

Herhangi bir nesne türüne bir işaretçi depolamak için bu genel işaretçiyi kullanabilirsiniz, ancak bununla olağan aritmetik işlemleri kullanamazsınız ve onu dikkate alamazsınız.


0

İşlev, rastgele bir türe bir gösterici alır ve böyle birini döndürür.


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.