Als ich heute den Code anderer las, sah ich so etwas wie void *func(void* i);: Was bedeutet das void*hier für den Funktionsnamen bzw. für den Variablentyp?
Wann müssen wir diese Art von Zeiger verwenden und wie wird er verwendet?
Nehmen Sie ein Stichwort von mallocund calloc. In der Manpage heißt es weiter: "... einen Zeiger auf den zugewiesenen Speicher zurückgeben, der für jeden integrierten Datentyp geeignet ausgerichtet ist."
Automat
Antworten:
175
Ein Zeiger auf voidist ein "generischer" Zeigertyp. A void *kann ohne explizite Umwandlung in einen anderen Zeigertyp konvertiert werden. Sie können eine dereferenzieren void *oder eine Zeigerarithmetik damit durchführen. Sie müssen es zuerst in einen Zeiger auf einen vollständigen Datentyp konvertieren.
void *wird häufig an Stellen verwendet, an denen Sie in der Lage sein müssen, mit verschiedenen Zeigertypen im selben Code zu arbeiten. Ein häufig genanntes Beispiel ist die Bibliotheksfunktion qsort:
baseist die Adresse eines Arrays, nmembist die Anzahl der Elemente im Array, sizeist die Größe jedes Elements und comparist ein Zeiger auf eine Funktion, die zwei Elemente des Arrays vergleicht. Es wird so genannt:
Die Array - Ausdrücke iArr, dArrund lArrimplizit von Array - Typ auf Zeiger Typen in dem Funktionsaufruf umgewandelt, und jedes ist implizit aus „Zeigern auf umgewandelt int/ double/ long“ auf „Zeiger void“.
Die Vergleichsfunktionen würden ungefähr so aussehen:
int compareInt(constvoid*lhs,constvoid*rhs){constint*x = lhs;// convert void * to int * by assignmentconstint*y = rhs;if(*x >*y)return1;if(*x ==*y)return0;return-1;}
Durch die Annahme void *, qsortkann mit Arrays jeder Art arbeiten.
Der Nachteil der Verwendung void *ist, dass Sie die Typensicherheit aus dem Fenster in den Gegenverkehr werfen. Es gibt nichts, was Sie davor schützen könnte, die falsche Vergleichsroutine zu verwenden:
compareInterwartet, dass seine Argumente auf ints verweisen, arbeitet aber tatsächlich mit doubles. Es gibt keine Möglichkeit, dieses Problem beim Kompilieren zu beheben. Sie werden nur mit einem falsch sortierten Array enden.
Es ist eigentlich nicht garantiert, dass a void*in einen Funktionszeiger umgewandelt werden kann. Aber für Datenzeiger gilt, was Sie gesagt haben.
Vatine
Bevor leere Zeiger verfügbar waren, wurde stattdessen "char *" verwendet. Aber void ist besser, da es nicht verwendet werden kann, um etwas direkt zu ändern.
user50619
22
Die Verwendung eines Leerzeichens * bedeutet, dass die Funktion einen Zeiger annehmen kann, der kein bestimmter Typ sein muss. Zum Beispiel haben Sie in Socket-Funktionen
send(void* pData,int nLength)
Dies bedeutet, dass Sie es beispielsweise auf viele Arten aufrufen können
char* data ="blah";
send(data, strlen(data));
POINT p;
p.x =1;
p.y =2;
send(&p,sizeof(POINT));
Das ist also ziemlich ähnlich wie bei Generika in anderen Sprachen, aber ohne Typprüfung, oder?
Flügelspieler Sendon
3
Ich nehme an, es wäre ähnlich, aber da es keine Typprüfung gibt, kann ein Fehler zu sehr seltsamen Ergebnissen führen oder das Programm zum Absturz bringen.
TheSteve
7
C ist in dieser Hinsicht bemerkenswert. Man kann sagen, voidNichts void*ist alles (kann alles sein).
Es ist nur so winzig *, was den Unterschied macht.
Rene hat darauf hingewiesen. A void *ist ein Zeiger auf einen Ort. Was dort zu "interpretieren" ist, bleibt dem Benutzer überlassen.
Dies ist die einzige Möglichkeit, in C undurchsichtige Typen zu haben. Sehr prominente Beispiele finden sich z. B. in glib- oder allgemeinen Datenstrukturbibliotheken. Es wird in "C-Schnittstellen und Implementierungen" sehr detailliert behandelt.
Ich schlage vor, Sie lesen das gesamte Kapitel und versuchen, das Konzept eines Zeigers zu verstehen, um "es zu bekommen".
ist ein 'Zeiger auf den Speicher ohne Annahmen, welcher Typ dort gespeichert ist'. Sie können beispielsweise verwenden, wenn Sie ein Argument an function übergeben möchten und dieses Argument von mehreren Typen sein kann. In function werden Sie jeden Typ behandeln.
Der ungültige Zeigertyp ist ein spezieller Zeigertyp. In C ++ stellt void das Fehlen eines Typs dar. Void-Zeiger sind also Zeiger, die auf einen Wert verweisen, der keinen Typ hat (und somit auch eine unbestimmte Länge und unbestimmte Dereferenzierungseigenschaften).
Auf diese Weise können ungültige Zeiger auf einen beliebigen Datentyp verweisen, von einem ganzzahligen Wert oder einem Float auf eine Zeichenfolge. Im Gegenzug haben sie jedoch eine große Einschränkung: Die Daten, auf die sie verweisen, können nicht direkt dereferenziert werden (was logisch ist, da wir keinen Typ haben, auf den wir dereferenzieren können), und aus diesem Grund müssen wir die Adresse immer in den ungültigen Zeiger auf setzen Ein anderer Zeigertyp, der auf einen konkreten Datentyp verweist, bevor er dereferenziert wird.
Ein ungültiger Zeiger wird als generischer Zeiger bezeichnet. Ich möchte mit einem Beispiel-Pthread-Szenario erklären.
Die Thread-Funktion hat den Prototyp als
void*(*start_routine)(void*)
Die pthread-API-Designer haben die Argument- und Rückgabewerte der Thread-Funktion berücksichtigt. Wenn diese Dinge generisch gemacht werden, können wir beim Senden als Argument cast to void * eingeben. In ähnlicher Weise kann der Rückgabewert aus void * abgerufen werden (aber ich habe nie Rückgabewerte aus der Thread-Funktion verwendet).
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);}
a void*ist ein Zeiger, aber der Typ, auf den er zeigt, ist nicht angegeben. Wenn Sie einen ungültigen Zeiger an eine Funktion übergeben, müssen Sie deren Typ kennen, um ihn später in der Funktion auf den richtigen Typ zurücksetzen zu können, um ihn zu verwenden. Sie werden Beispiele sehen pthreads, die Funktionen mit genau dem Prototyp in Ihrem Beispiel verwenden, die als Thread-Funktion verwendet werden. Sie können das void*Argument dann als Zeiger auf einen generischen Datentyp Ihrer Wahl verwenden und es dann auf diesen Typ zurücksetzen, um ihn in Ihrer Thread-Funktion zu verwenden. Sie müssen jedoch vorsichtig sein, wenn Sie ungültige Zeiger verwenden. Wenn Sie jedoch nicht auf einen Zeiger seines wahren Typs zurückgreifen, können alle möglichen Probleme auftreten.
Ein Zeiger auf voidkann in oder von einem Zeiger auf einen beliebigen Objekttyp konvertiert werden. Ein Zeiger auf einen beliebigen Objekttyp kann in einen Zeiger auf ungültig und wieder zurück konvertiert werden. Das Ergebnis muss mit dem ursprünglichen Zeiger verglichen werden.
Sie können diesen generischen Zeiger verwenden, um einen Zeiger auf einen beliebigen Objekttyp zu speichern, aber Sie können damit keine üblichen arithmetischen Operationen verwenden und ihn nicht zurückstellen.
malloc
undcalloc
. In der Manpage heißt es weiter: "... einen Zeiger auf den zugewiesenen Speicher zurückgeben, der für jeden integrierten Datentyp geeignet ausgerichtet ist."Antworten:
Ein Zeiger auf
void
ist ein "generischer" Zeigertyp. Avoid *
kann ohne explizite Umwandlung in einen anderen Zeigertyp konvertiert werden. Sie können eine dereferenzierenvoid *
oder eine Zeigerarithmetik damit durchführen. Sie müssen es zuerst in einen Zeiger auf einen vollständigen Datentyp konvertieren.void *
wird häufig an Stellen verwendet, an denen Sie in der Lage sein müssen, mit verschiedenen Zeigertypen im selben Code zu arbeiten. Ein häufig genanntes Beispiel ist die Bibliotheksfunktionqsort
:base
ist die Adresse eines Arrays,nmemb
ist die Anzahl der Elemente im Array,size
ist die Größe jedes Elements undcompar
ist ein Zeiger auf eine Funktion, die zwei Elemente des Arrays vergleicht. Es wird so genannt:Die Array - Ausdrücke
iArr
,dArr
undlArr
implizit von Array - Typ auf Zeiger Typen in dem Funktionsaufruf umgewandelt, und jedes ist implizit aus „Zeigern auf umgewandeltint
/double
/long
“ auf „Zeigervoid
“.Die Vergleichsfunktionen würden ungefähr so aussehen:
Durch die Annahme
void *
,qsort
kann mit Arrays jeder Art arbeiten.Der Nachteil der Verwendung
void *
ist, dass Sie die Typensicherheit aus dem Fenster in den Gegenverkehr werfen. Es gibt nichts, was Sie davor schützen könnte, die falsche Vergleichsroutine zu verwenden:compareInt
erwartet, dass seine Argumente aufint
s verweisen, arbeitet aber tatsächlich mitdouble
s. Es gibt keine Möglichkeit, dieses Problem beim Kompilieren zu beheben. Sie werden nur mit einem falsch sortierten Array enden.quelle
void*
in einen Funktionszeiger umgewandelt werden kann. Aber für Datenzeiger gilt, was Sie gesagt haben.Die Verwendung eines Leerzeichens * bedeutet, dass die Funktion einen Zeiger annehmen kann, der kein bestimmter Typ sein muss. Zum Beispiel haben Sie in Socket-Funktionen
Dies bedeutet, dass Sie es beispielsweise auf viele Arten aufrufen können
quelle
C ist in dieser Hinsicht bemerkenswert. Man kann sagen,
void
Nichtsvoid*
ist alles (kann alles sein).Es ist nur so winzig
*
, was den Unterschied macht.Rene hat darauf hingewiesen. A
void *
ist ein Zeiger auf einen Ort. Was dort zu "interpretieren" ist, bleibt dem Benutzer überlassen.Dies ist die einzige Möglichkeit, in C undurchsichtige Typen zu haben. Sehr prominente Beispiele finden sich z. B. in glib- oder allgemeinen Datenstrukturbibliotheken. Es wird in "C-Schnittstellen und Implementierungen" sehr detailliert behandelt.
Ich schlage vor, Sie lesen das gesamte Kapitel und versuchen, das Konzept eines Zeigers zu verstehen, um "es zu bekommen".
quelle
ist ein 'Zeiger auf den Speicher ohne Annahmen, welcher Typ dort gespeichert ist'. Sie können beispielsweise verwenden, wenn Sie ein Argument an function übergeben möchten und dieses Argument von mehreren Typen sein kann. In function werden Sie jeden Typ behandeln.
quelle
Sie können sich diesen Artikel über Zeiger http://www.cplusplus.com/doc/tutorial/pointers/ ansehen und das Kapitel lesen: Zeiger ungültig machen .
Dies funktioniert auch für die Sprache C.
quelle
Ein ungültiger Zeiger wird als generischer Zeiger bezeichnet. Ich möchte mit einem Beispiel-Pthread-Szenario erklären.
Die Thread-Funktion hat den Prototyp als
Die pthread-API-Designer haben die Argument- und Rückgabewerte der Thread-Funktion berücksichtigt. Wenn diese Dinge generisch gemacht werden, können wir beim Senden als Argument cast to void * eingeben. In ähnlicher Weise kann der Rückgabewert aus void * abgerufen werden (aber ich habe nie Rückgabewerte aus der Thread-Funktion verwendet).
quelle
pthread_exit(NULL);
stattreturn 0;
am Ende von main?a
void*
ist ein Zeiger, aber der Typ, auf den er zeigt, ist nicht angegeben. Wenn Sie einen ungültigen Zeiger an eine Funktion übergeben, müssen Sie deren Typ kennen, um ihn später in der Funktion auf den richtigen Typ zurücksetzen zu können, um ihn zu verwenden. Sie werden Beispiele sehenpthreads
, die Funktionen mit genau dem Prototyp in Ihrem Beispiel verwenden, die als Thread-Funktion verwendet werden. Sie können dasvoid*
Argument dann als Zeiger auf einen generischen Datentyp Ihrer Wahl verwenden und es dann auf diesen Typ zurücksetzen, um ihn in Ihrer Thread-Funktion zu verwenden. Sie müssen jedoch vorsichtig sein, wenn Sie ungültige Zeiger verwenden. Wenn Sie jedoch nicht auf einen Zeiger seines wahren Typs zurückgreifen, können alle möglichen Probleme auftreten.quelle
C11 Standard (n1570) §6.2.2.3 al1 p55 sagt:
Sie können diesen generischen Zeiger verwenden, um einen Zeiger auf einen beliebigen Objekttyp zu speichern, aber Sie können damit keine üblichen arithmetischen Operationen verwenden und ihn nicht zurückstellen.
quelle
Die Funktion nimmt einen Zeiger auf einen beliebigen Typ und gibt einen solchen zurück.
quelle
Dies bedeutet, dass Sie über diesen Link weitere Informationen zum Zeiger http://www.cprogramming.com/tutorial/c/lesson6.html erhalten können
quelle