[C++] Detaillierte Erklärung von void*


Ich habe heute einen Code gesehen, den ich sehr interessant fand.

void* say_hello(void* args)
{
    cout << "Hello World!" << endl;
    return 0;
}

Sie sehen oft „Leere“, aber können Sie sagen, was die konkrete Bedeutung von „Leere*“ ist?

1. Was ist ungültig*?

Wir wissen, dass C++ eine stark typisierte Sprache ist und die Größe der Zeigertypen gleich ist, dh die Größe von int* und long long* ist gleich.

sizeof(int*) == sizeof(long long*)

Da also jeder ein Zeigertyp ist, warum gibt es dann so viele Kategorien? Mit anderen Worten: Welche Rolle spielen die spezifischen Typen int und longlong vor dem *-Zeichen? Schauen wir uns das Bild unten an:
Bild

Verschiedene Zeigertypen legen bei +1 unterschiedliche „Entfernungen“ zurück. Wenn wir seinen Typ zuvor angegeben haben. Das kommt der Bestimmung seiner „Sprungkraft“ gleich. „Sprungleistung“ ist wie der int-Sprung um 4 Bytes in der obigen Abbildung, aber der doppelte Sprung um 8 Bytes.

Basierend auf diesem Verständnis kann void * nun definiert werden:void * ist ein Zeiger mit unbestimmter Sprungkraft

Das ist die Magie daran: Wir können es steuern, um es bei Bedarf in den erforderlichen Typ zu implementieren. Der Vorteil dabei ist: Code beim Programmieren sparen und generische Programmierung realisieren.

2. Ausführliche Erklärung der Nichtigkeit*

1️⃣: void* kann auf die Adresse eines beliebigen Datentyps zeigen, aber ein typisierter Zeiger kann nicht nach Belieben auf die Adresse von void* zeigen:

float f = 5.5;
float* pf = &f;
void* pv = pf;
float* pf2 = pv;//编译错误,有类型的指针变量不能指向void*变量

2️⃣: Der void*-Zeiger kann nur nach erzwungener Typkonvertierung einen normalen Wert erhalten:

int main(int argc, const char * argv[]) {
    
    float f = 5.5;
    float* pf = &f;
    void* pv;
    
    pv = pf; //这句是可以的
    
    cout<<*pv<<endl;  //编译错误,这样直接对pv取值是错误的
    cout<<*(float*)pv<<endl;  //强制类型转换后可以取值
    
    return 0;
}

Nach dem Festlegen von pv = pf zeigen pv und pf auf dieselbe Adresse und haben denselben Wert, aber ihre Typen sind unterschiedlich. Als Gleitkommazeiger kann pf Gleitkommazahlen direkt abrufen.Pv muss jedoch zur Typkonvertierung gezwungen werden, bevor es den Wert erhalten kann. Das heißt, ein void*-Zeiger muss vorhanden sein erzwungen werden. Dies macht erst nach der Typkonvertierung Sinn.

int main(int argc, const char * argv[]) {
    
    float f = 5.5;
    float* pf = &f;
    void* pv;
 
    pv = pf;
   
    cout<<*(float*)pv<<endl;  //强制类型转换后可以取值,值为5.5
    cout<<*(int*)pv<<endl; //强制类型转换,值为1085276160
    cout<<(int)(*(float*)pv)<<endl;//取值后再次类型转换,值为5
    
    return 0;
}

Es ist auch falsch, einen void-Zeiger auf einen Float-Wert in int* umzuwandeln. Mit anderen Worten, welche Art von Variable an der Adresse gespeichert ist muss in welche Art von Zeiger konvertiert werden, andernfalls tritt ein Operationsfehler auf.

3️⃣: void*-Zeigervariablen können wie gewöhnliche Zeiger mit dem Wert 0 oder NULL initialisiert werden, was einen Nullzeiger anzeigt

void* pv = 0; 
void* pv2 = NULL;
cout<<pv <<endl; //值为0x0
cout<<pv2<<endl; //值为0x0

4️⃣: Wenn der void *-Zeiger als Eingabe und Ausgabe einer Funktion verwendet wird, bedeutet dies, dass er jede Art von Eingabezeiger akzeptieren und jede Art von Zeiger ausgeben kann.

void* test(void* a)
{
    return a;
}
 
int main() {
 
    static int a = 5;
    int* pi = &a;
    
    cout<<pi<<endl;              //值为0x100001060
    cout<<test(pi)<<endl;        //值为0x100001060
    cout<<test((void*)pi)<<endl; //值为0x100001060
}

Wenn der Eingabetyp der Funktion void* ist, sollte die Funktion tatsächlich einen Adresswert empfangen, da sie beim Aufruf als Wert übergeben wird. Dieser Wert kann von beliebigem Typ sein.

int a = 5;
int* pi = &a;
 
void* test()
{
    return pi; 
}
 
int main() {
    cout<<test()<<endl;        //值为0x100001060
}

3. Der Unterschied zwischen nichtig und nichtig

Schauen wir uns noch einmal die ursprüngliche Funktion an:

//返回了一个空指针
void* say_hello(void* args)
{
    cout << "Hello World!" << endl;
    return 0;
}

//没有返回值
void say_hello(void* args)
{
    cout << "Hello World!" << endl;
   return;
}

Tatsächlich ist der von den beiden Funktionen implementierte Inhalt derselbe. Aber die Funktion vom Rückgabetyp void* gibt einen Nullzeiger zurück und der Typ void hat keinen Rückgabewert.

4. Anwendungsszenarien

4.1 Der Typ ist unsicher, wenn Funktionsparameter übergeben werden oder mehrere Parametertypen unterstützt werden müssen;

void function(int dataType, void* data) {
 
    // 根据dataType的不同值,进行不同的转换
    switch (dataType) {
 
        case 0:
            int* a = (int*)data;
 
        case 1:
            char* a = (char*)data;
 
        ...
    }
}

4.2 Wenn der Rückgabewert einer Funktion nicht den Typ berücksichtigt, sondern sich um die Größe kümmert

void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

Memcpy und Memset empfangen externe Zeiger jeglicher Art. Dies ist sinnvoll und notwendig, da es sich um Speicheroperationsfunktionen handelt, die mit Bits arbeiten. Es macht keinen Sinn, den Datentyp zu berücksichtigen.

int *a=NULL;
 
a=(int *)malloc(sizeof(int));//返回的是void*,所以赋值给其他指针类型要强转一下

Ebenso konzentriert sich die malloc-Funktion nur darauf, wie viel Speicher Sie benötigen. Wie Sie ihn aufteilen, bleibt Ihnen überlassen, Sie müssen jedoch explizit angeben, wie Sie ihn aufteilen. Die Syntaxanforderung ist hier obligatorisch und die Konvertierung vom Typ void * in andere Typen muss eine erzwungene Typkonvertierung sein.

5. Zusammenfassung

  1. Der Zeiger vom Typ void* ist im Wesentlichen ein Übergangszeigerzustand und muss einem Typ zugewiesen werden (erzwungene Typkonvertierung), bevor er normal verwendet werden kann. Der Bereich von void * ist größer und daher gezwungen, seinen Bereich einzuschränken.
  2. Es ist nur eine einseitige Typkonvertierung möglich. void* kann in andere Typen konvertiert werden, Typen können jedoch nicht in void* konvertiert werden.
  3. Es lässt sich auch sehr einfach als Eingabe- und Ausgabeparameter bei Funktionsaufrufen verwenden. Sie können Zeiger beliebiger Art flexibel verwenden, um zu vermeiden, dass nur Zeiger fester Art verwendet werden.

Guess you like

Origin blog.csdn.net/weixin_43717839/article/details/134293471