Zeigerarithmetik für Void-Zeiger in C

Lesezeit: 7 Minuten

Benutzeravatar von Siva Sankaran
Shiva Sankaran

Wenn ein Zeiger auf einen bestimmten Typ (z int, char, float, ..) erhöht wird, wird sein Wert um die Größe dieses Datentyps erhöht. Wenn ein void Zeiger, der auf Daten der Größe zeigt x inkrementiert wird, wie kommt es zum Punkt x Bytes voraus? Woher weiß der Compiler, dass er hinzufügen soll x zum Wert des Zeigers?

  • mögliches Duplikat von Error Performing Pointer Arithmetic on void * in MSVC

    – Karl Norum

    19. August 2010 um 21:43 Uhr

  • Die Frage klingt, als ob sie davon ausgeht, dass der Compiler (/run-time) weiß, auf welchen Objekttyp der Zeiger gesetzt wurde, und seine Größe zum Zeiger hinzufügt. Das ist ein völliger Irrtum: Es kennt nur die Adresse.

    – PJTrail

    5. Mai 2015 um 21:22 Uhr

  • “Wenn ein void Zeiger, der auf Daten der Größe zeigt x inkrementiert wird, wie kommt es zum Punkt x Bytes voraus?” Tut es nicht. Warum können Leute, die solche Fragen haben, sie nicht testen, bevor sie sie stellen – weißt du, zumindest bis zum absoluten Minimum, wo sie überprüfen, ob sie tatsächlich kompiliert werden, was dies nicht tut. -1, Ich kann nicht glauben, dass dies +100 und -0 hat.

    – Unterstrich_d

    20. August 2016 um 6:30 Uhr

  • @underscore_d, weil es eine gute Frage ist. Wir müssen wissen, WARUM es nicht kompiliert. Es wäre vollkommen logisch anzunehmen, dass das Hinzufügen von 1 zu void * erhöht die Adresse um 1 Byte (wie char *). Doch der dumme unlogische Standard, dass es “illegal” ist, wird jedem ohne Grund aufgezwungen.

    – Wissenschaftsentdecker

    18. Juli um 14:16 Uhr

Benutzeravatar von Sadeq
Sadeq

Schlussbemerkung: Arithmetik auf a void* ist illegal sowohl in C als auch in C++.

GCC erlaubt es als Erweiterung, siehe Arithmetik an void– und Funktionszeiger (Beachten Sie, dass dieser Abschnitt Teil des Kapitels „C-Erweiterungen“ des Handbuchs ist). Clang und ICC erlauben es wahrscheinlich void* Arithmetik zum Zwecke der Kompatibilität mit GCC. Andere Compiler (z. B. MSVC) lassen Arithmetik nicht zu void*und GCC verbietet es, wenn die -pedantic-errors Flag angegeben ist, oder wenn die -Werror-pointer-arith Flag angegeben ist (dieses Flag ist nützlich, wenn Ihre Codebasis auch mit MSVC kompiliert werden muss).

Der C-Standard spricht

Die Zitate stammen aus dem n1256-Entwurf.

In der Standardbeschreibung der Additionsoperation heißt es:

6.5.6-2: Für die Addition müssen entweder beide Operanden vom arithmetischen Typ sein, oder ein Operand soll ein Zeiger auf einen Objekttyp sein und der andere soll vom ganzzahligen Typ sein.

Hier stellt sich also die Frage, ob void* ein Zeiger auf einen “Objekttyp” ist, oder äquivalent, ob void ist ein “Objekttyp”. Die Definition für “Objekttyp” lautet:

6.2.5.1: Typen werden unterteilt in Objekttypen (Typen, die Objekte vollständig beschreiben) , Funktionstypen (Typen, die Funktionen beschreiben) und unvollständige Typen (Typen, die Objekte beschreiben, denen jedoch Informationen fehlen, die zur Bestimmung ihrer Größe erforderlich sind).

Und der Standard definiert void wie:

6.2.5-19: Die void Typ besteht aus einer leeren Menge von Werten; es ist ein unvollständiger Typ, der nicht vervollständigt werden kann.

Seit void ein unvollständiger Typ ist, ist es kein Objekttyp. Daher ist es kein gültiger Operand für eine Additionsoperation.

Daher können Sie auf a keine Zeigerarithmetik durchführen void Zeiger.

Anmerkungen

Ursprünglich dachte man das void* Arithmetik war aufgrund dieser Abschnitte des C-Standards erlaubt:

6.2.5-27: Ein Zeiger auf void muss haben gleiche Darstellung und Ausrichtung
Anforderungen als Zeiger auf einen Zeichentyp.

Jedoch,

Dieselbe Darstellung und Ausrichtung
Anforderungen sollen Austauschbarkeit als Argumente für Funktionen, Rückgabewerte von Funktionen und Mitglieder von Vereinigungen implizieren.

Das bedeutet also printf("%s", x) hat die gleiche Bedeutung, ob x Typ hat char* oder void*aber das heißt nicht, dass man mit a rechnen kann void*.

  • Aus dem C99-Standard: (6.5.6.2) Für die Addition sollen entweder beide Operanden vom arithmetischen Typ sein, oder ein Operand soll ein Zeiger auf einen Objekttyp sein und der andere soll vom ganzzahligen Typ sein. (6.2.5.19) Der void-Typ umfasst einen leeren Satz von Werten; es ist ein unvollständiger Typ, der nicht vervollständigt werden kann. Ich denke, das macht es deutlich void* Zeigerarithmetik ist nicht erlaubt. GCC hat eine Verlängerung das erlaubt dies zu tun.

    – mtvec

    19. August 2010 um 18:29 Uhr

  • Wenn Sie Ihre Antwort nicht mehr für nützlich halten, können Sie sie einfach löschen.

    – Café

    20. August 2010 um 1:28 Uhr

  • Diese Antwort war nützlich, obwohl sie sich als falsch herausstellte, da sie den schlüssigen Beweis dafür enthält, dass void-Zeiger nicht für Arithmetik gedacht sind.

    – Ben Flynn

    17. Oktober 2012 um 21:48 Uhr

  • Dies ist eine gute Antwort, sie hat die richtige Schlussfolgerung und die notwendigen Zitate, aber Leute, die zu dieser Frage kamen, kamen zu der falschen Schlussfolgerung, weil sie die Antwort nicht bis zum Ende gelesen hatten. Ich habe dies bearbeitet, um es deutlicher zu machen.

    – Dietrich Ep

    11. Mai 2013 um 2:37 Uhr

  • Oh, aber für die Zeigeraddition, jetzt “ein Operand soll ein Zeiger auf einen vollständigen Objekttyp sein und der andere soll einen ganzzahligen Typ haben.”. Ich denke also, dass das Hinzufügen eines Zeigers mit einem void*-Zeiger immer noch ein undefiniertes Verhalten ist.

    – Steve Siegel

    14. Dezember 2016 um 1:55 Uhr

Zeigerarithmetik ist nicht erlaubt void* Zeiger.

  • +1 Zeigerarithmetik ist nur für Zeiger auf (vollständig) definiert Objekttypen. void ist ein unvollständiger Typ die per definitionem nie abgeschlossen werden kann.

    – schott

    19. August 2010 um 15:33 Uhr

  • @Schot: Genau. Darüber hinaus ist die Zeigerarithmetik nur für einen Zeiger auf ein Element eines Array-Objekts definiert und nur dann, wenn das Ergebnis der Operation ein Zeiger auf ein Element in demselben Array oder eines nach dem letzten Element dieses Arrays wäre. Wenn diese Bedingungen nicht erfüllt sind, handelt es sich um ein undefiniertes Verhalten. (Vom C99-Standard 6.5.6.8)

    – mtvec

    19. August 2010 um 18:39 Uhr


  • Anscheinend ist es bei gcc 7.3.0 nicht so. Der Compiler akzeptiert p + 1024, wobei p leer ist*. Und das Ergebnis ist dasselbe wie ((char *)p) + 1024

    – uuu777

    24. März 2019 um 2:27 Uhr

  • @zzz777 Es ist eine GCC-Erweiterung, siehe den Link in der am häufigsten bewerteten Antwort.

    – Ruslan

    3. April 2020 um 18:48 Uhr

wandeln Sie es in einen Zeichenzeiger um und inkrementieren Sie Ihren Zeiger x Bytes vorwärts.

  • Wenn Sie Ihre Sortierfunktion schreiben, die gem man 3 qsort sollte die haben void qsort(void *base, size_t nmemb, size_t size, [snip])dann haben Sie keine Möglichkeit, den “richtigen Typ” zu kennen

    – alisianoi

    14. Mai 2017 um 15:30 Uhr

  • Wenn Sie so etwas wie das container_of-Makro des Linux-Kernels schreiben, brauchen Sie außerdem eine Möglichkeit, das Packen von Strukturen durch verschiedene Compiler zu kompensieren. Zum Beispiel bei dieser Struktur: … typedef struct a_ { x X; y Y; } a; … Wenn Sie dann eine Variable haben y *B = (something) und Sie möchten einen Zeiger auf die Einschließung von B a struct (vorausgesetzt, es existiert), dann müssen Sie am Ende so etwas tun: … a *A = (a*)(((char*)B) - offsetof(a, Y)); … Wenn Sie stattdessen Folgendes tun: … a *A = (a*)(((x*)B)-1); … dann können Sie böse Überraschungen erleben oder auch nicht!

    – Chadjoan

    8. Oktober 2020 um 11:28 Uhr

Benutzeravatar von msc
msc

Das C-Standard erlaubt nicht Leere Zeigerarithmetik. Jedoch, GNU-C ist unter Berücksichtigung der Größe zulässig Leere ist 1.

C11-Standard §6.2.5

Absatz – 19

Das void Typ besteht aus einer leeren Menge von Werten; es ist ein unvollständiger Objekttyp das lässt sich nicht abschließen.

Das folgende Programm funktioniert im GCC-Compiler einwandfrei.

#include<stdio.h>

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

Möglicherweise erzeugen andere Compiler einen Fehler.

Sie können keine Zeigerarithmetik durchführen void * Typen, genau aus diesem Grund!

Benutzeravatar von tchrist
tchrist

Void-Zeiger können auf jeden Speicherblock zeigen. Daher weiß der Compiler nicht, wie viele Bytes zu inkrementieren/dekrementieren sind, wenn wir versuchen, eine Zeigerarithmetik für einen leeren Zeiger zu verwenden. Daher müssen void-Zeiger zuerst in einen bekannten Typ umgewandelt werden, bevor sie in eine Zeigerarithmetik einbezogen werden können.

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.

Benutzeravatar von Jakob
Jakob

Sie müssen es in einen anderen Zeigertyp umwandeln, bevor Sie Zeigerarithmetik durchführen.

1426040cookie-checkZeigerarithmetik für Void-Zeiger in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy